feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -1,8 +1,12 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
from methods import print_error
Import("env")
env.drivers_sources = []
supported = env.get("supported", [])
# OS drivers
SConscript("unix/SCsub")
@ -10,15 +14,21 @@ SConscript("windows/SCsub")
# Sounds drivers
SConscript("alsa/SCsub")
SConscript("coreaudio/SCsub")
if env["platform"] == "ios" or env["platform"] == "macos":
SConscript("coreaudio/SCsub")
SConscript("pulseaudio/SCsub")
if env["platform"] == "windows":
SConscript("wasapi/SCsub")
if not env.msvc:
SConscript("backtrace/SCsub")
if env["xaudio2"]:
if "xaudio2" not in supported:
print_error("Target platform '{}' does not support the XAudio2 audio driver".format(env["platform"]))
Exit(255)
SConscript("xaudio2/SCsub")
# Shared Apple platform drivers
if env["platform"] in ["macos", "ios"]:
SConscript("apple/SCsub")
# Midi drivers
SConscript("alsamidi/SCsub")
SConscript("coremidi/SCsub")
@ -28,11 +38,19 @@ SConscript("winmidi/SCsub")
if env["vulkan"]:
SConscript("vulkan/SCsub")
if env["d3d12"]:
if "d3d12" not in supported:
print_error("Target platform '{}' does not support the D3D12 rendering driver".format(env["platform"]))
Exit(255)
SConscript("d3d12/SCsub")
if env["opengl3"]:
SConscript("gl_context/SCsub")
SConscript("gles3/SCsub")
SConscript("egl/SCsub")
if env["metal"]:
if "metal" not in supported:
print_error("Target platform '{}' does not support the Metal rendering driver".format(env["platform"]))
Exit(255)
SConscript("metal/SCsub")
# Core dependencies
SConscript("png/SCsub")

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -80,7 +80,7 @@ Error AudioDriverALSA::init_output_device() {
status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
} else {
String device = output_device_name;
int pos = device.find(";");
int pos = device.find_char(';');
if (pos != -1) {
device = device.substr(0, pos);
}

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -33,7 +33,6 @@
#include "midi_driver_alsamidi.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
#include <errno.h>

View file

@ -45,8 +45,6 @@
#include <alsa/asoundlib.h>
#endif
#include <stdio.h>
class MIDIDriverALSAMidi : public MIDIDriver {
Thread thread;
Mutex mutex;

View file

@ -0,0 +1,7 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
# Driver source files
env.add_source_files(env.drivers_sources, "*.mm")

View file

@ -0,0 +1,71 @@
/**************************************************************************/
/* joypad_apple.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "core/input/input.h"
#define Key _QKey
#import <GameController/GameController.h>
#undef Key
@class GCController;
class RumbleContext;
struct GameController {
int joy_id;
GCController *controller;
RumbleContext *rumble_context API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) = nil;
NSInteger ff_effect_timestamp = 0;
bool force_feedback = false;
GameController(int p_joy_id, GCController *p_controller);
~GameController();
};
class JoypadApple {
private:
id<NSObject> connect_observer = nil;
id<NSObject> disconnect_observer = nil;
HashMap<int, GameController *> joypads;
HashMap<GCController *, int> controller_to_joy_id;
GCControllerPlayerIndex get_free_player_index();
void add_joypad(GCController *p_controller);
void remove_joypad(GCController *p_controller);
public:
JoypadApple();
~JoypadApple();
void joypad_vibration_start(GameController &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0));
void joypad_vibration_stop(GameController &p_joypad, uint64_t p_timestamp) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0));
void process_joypads();
};

View file

@ -0,0 +1,427 @@
/**************************************************************************/
/* joypad_apple.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "joypad_apple.h"
#include <CoreHaptics/CoreHaptics.h>
#import <os/log.h>
#include "core/config/project_settings.h"
#include "main/main.h"
class API_AVAILABLE(macos(11), ios(14.0), tvos(14.0)) RumbleMotor {
CHHapticEngine *engine;
id<CHHapticPatternPlayer> player;
bool is_started;
RumbleMotor(GCController *p_controller, GCHapticsLocality p_locality) {
engine = [p_controller.haptics createEngineWithLocality:p_locality];
engine.autoShutdownEnabled = YES;
}
public:
static RumbleMotor *create(GCController *p_controller, GCHapticsLocality p_locality) {
if ([p_controller.haptics.supportedLocalities containsObject:p_locality]) {
return memnew(RumbleMotor(p_controller, p_locality));
}
return nullptr;
}
_ALWAYS_INLINE_ bool has_active_player() {
return player != nil;
}
void execute_pattern(CHHapticPattern *p_pattern) {
NSError *error;
if (!is_started) {
ERR_FAIL_COND_MSG(![engine startAndReturnError:&error], "Couldn't start controller haptic engine: " + String::utf8(error.localizedDescription.UTF8String));
is_started = YES;
}
player = [engine createPlayerWithPattern:p_pattern error:&error];
ERR_FAIL_COND_MSG(error, "Couldn't create controller haptic pattern player: " + String::utf8(error.localizedDescription.UTF8String));
ERR_FAIL_COND_MSG(![player startAtTime:CHHapticTimeImmediate error:&error], "Couldn't execute controller haptic pattern: " + String::utf8(error.localizedDescription.UTF8String));
}
void stop() {
id<CHHapticPatternPlayer> old_player = player;
player = nil;
NSError *error;
ERR_FAIL_COND_MSG(![old_player stopAtTime:CHHapticTimeImmediate error:&error], "Couldn't stop controller haptic pattern: " + String::utf8(error.localizedDescription.UTF8String));
}
};
class API_AVAILABLE(macos(11), ios(14.0), tvos(14.0)) RumbleContext {
RumbleMotor *weak_motor;
RumbleMotor *strong_motor;
public:
RumbleContext(GCController *p_controller) {
weak_motor = RumbleMotor::create(p_controller, GCHapticsLocalityRightHandle);
strong_motor = RumbleMotor::create(p_controller, GCHapticsLocalityLeftHandle);
}
~RumbleContext() {
if (weak_motor) {
memdelete(weak_motor);
}
if (strong_motor) {
memdelete(strong_motor);
}
}
_ALWAYS_INLINE_ bool has_motors() {
return weak_motor != nullptr && strong_motor != nullptr;
}
_ALWAYS_INLINE_ bool has_active_players() {
if (!has_motors()) {
return false;
}
return (weak_motor && weak_motor->has_active_player()) || (strong_motor && strong_motor->has_active_player());
}
void stop() {
if (weak_motor) {
weak_motor->stop();
}
if (strong_motor) {
strong_motor->stop();
}
}
void play_weak_pattern(CHHapticPattern *p_pattern) {
if (weak_motor) {
weak_motor->execute_pattern(p_pattern);
}
}
void play_strong_pattern(CHHapticPattern *p_pattern) {
if (strong_motor) {
strong_motor->execute_pattern(p_pattern);
}
}
};
GameController::GameController(int p_joy_id, GCController *p_controller) :
joy_id(p_joy_id), controller(p_controller) {
force_feedback = NO;
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
if (controller.haptics != nil) {
// Create a rumble context for the controller.
rumble_context = memnew(RumbleContext(p_controller));
// If the rumble motors aren't available, disable force feedback.
force_feedback = rumble_context->has_motors();
}
}
int l_joy_id = joy_id;
auto BUTTON = [l_joy_id](JoyButton p_button) {
return ^(GCControllerButtonInput *button, float value, BOOL pressed) {
Input::get_singleton()->joy_button(l_joy_id, p_button, pressed);
};
};
if (controller.extendedGamepad != nil) {
GCExtendedGamepad *gamepad = controller.extendedGamepad;
gamepad.buttonA.pressedChangedHandler = BUTTON(JoyButton::A);
gamepad.buttonB.pressedChangedHandler = BUTTON(JoyButton::B);
gamepad.buttonX.pressedChangedHandler = BUTTON(JoyButton::X);
gamepad.buttonY.pressedChangedHandler = BUTTON(JoyButton::Y);
gamepad.leftShoulder.pressedChangedHandler = BUTTON(JoyButton::LEFT_SHOULDER);
gamepad.rightShoulder.pressedChangedHandler = BUTTON(JoyButton::RIGHT_SHOULDER);
gamepad.dpad.up.pressedChangedHandler = BUTTON(JoyButton::DPAD_UP);
gamepad.dpad.down.pressedChangedHandler = BUTTON(JoyButton::DPAD_DOWN);
gamepad.dpad.left.pressedChangedHandler = BUTTON(JoyButton::DPAD_LEFT);
gamepad.dpad.right.pressedChangedHandler = BUTTON(JoyButton::DPAD_RIGHT);
gamepad.leftThumbstick.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::LEFT_X, xValue);
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::LEFT_Y, -yValue);
};
gamepad.rightThumbstick.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::RIGHT_X, xValue);
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::RIGHT_Y, -yValue);
};
gamepad.leftTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::TRIGGER_LEFT, value);
};
gamepad.rightTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::TRIGGER_RIGHT, value);
};
if (@available(macOS 10.14.1, iOS 12.1, tvOS 12.1, *)) {
gamepad.leftThumbstickButton.pressedChangedHandler = BUTTON(JoyButton::LEFT_STICK);
gamepad.rightThumbstickButton.pressedChangedHandler = BUTTON(JoyButton::RIGHT_STICK);
}
if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
gamepad.buttonOptions.pressedChangedHandler = BUTTON(JoyButton::BACK);
gamepad.buttonMenu.pressedChangedHandler = BUTTON(JoyButton::START);
}
if (@available(macOS 11, iOS 14.0, tvOS 14.0, *)) {
gamepad.buttonHome.pressedChangedHandler = BUTTON(JoyButton::GUIDE);
if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
xboxGamepad.paddleButton1.pressedChangedHandler = BUTTON(JoyButton::PADDLE1);
xboxGamepad.paddleButton2.pressedChangedHandler = BUTTON(JoyButton::PADDLE2);
xboxGamepad.paddleButton3.pressedChangedHandler = BUTTON(JoyButton::PADDLE3);
xboxGamepad.paddleButton4.pressedChangedHandler = BUTTON(JoyButton::PADDLE4);
}
}
if (@available(macOS 12, iOS 15.0, tvOS 15.0, *)) {
if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
xboxGamepad.buttonShare.pressedChangedHandler = BUTTON(JoyButton::MISC1);
}
}
} else if (controller.microGamepad != nil) {
GCMicroGamepad *gamepad = controller.microGamepad;
gamepad.buttonA.pressedChangedHandler = BUTTON(JoyButton::A);
gamepad.buttonX.pressedChangedHandler = BUTTON(JoyButton::X);
gamepad.dpad.up.pressedChangedHandler = BUTTON(JoyButton::DPAD_UP);
gamepad.dpad.down.pressedChangedHandler = BUTTON(JoyButton::DPAD_DOWN);
gamepad.dpad.left.pressedChangedHandler = BUTTON(JoyButton::DPAD_LEFT);
gamepad.dpad.right.pressedChangedHandler = BUTTON(JoyButton::DPAD_RIGHT);
}
// TODO: Need to add support for controller.motion which gives us access to
// the orientation of the device (if supported).
}
GameController::~GameController() {
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
if (rumble_context) {
memdelete(rumble_context);
}
}
}
JoypadApple::JoypadApple() {
connect_observer = [NSNotificationCenter.defaultCenter
addObserverForName:GCControllerDidConnectNotification
object:nil
queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *notification) {
GCController *controller = notification.object;
if (!controller) {
return;
}
add_joypad(controller);
}];
disconnect_observer = [NSNotificationCenter.defaultCenter
addObserverForName:GCControllerDidDisconnectNotification
object:nil
queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *notification) {
GCController *controller = notification.object;
if (!controller) {
return;
}
remove_joypad(controller);
}];
if (@available(macOS 11.3, iOS 14.5, tvOS 14.5, *)) {
GCController.shouldMonitorBackgroundEvents = YES;
}
}
JoypadApple::~JoypadApple() {
for (KeyValue<int, GameController *> &E : joypads) {
memdelete(E.value);
E.value = nullptr;
}
[NSNotificationCenter.defaultCenter removeObserver:connect_observer];
[NSNotificationCenter.defaultCenter removeObserver:disconnect_observer];
}
// Finds the rightmost set bit in a number, n.
// variation of https://www.geeksforgeeks.org/position-of-rightmost-set-bit/
int rightmost_one(int n) {
return __builtin_ctz(n & -n) + 1;
}
GCControllerPlayerIndex JoypadApple::get_free_player_index() {
// player_set will be a bitfield where each bit represents a player index.
__block uint32_t player_set = 0;
for (const KeyValue<GCController *, int> &E : controller_to_joy_id) {
player_set |= 1U << E.key.playerIndex;
}
// invert, as we want to find the first unset player index.
int n = rightmost_one((int)(~player_set));
if (n >= 5) {
return GCControllerPlayerIndexUnset;
}
return (GCControllerPlayerIndex)(n - 1);
}
void JoypadApple::add_joypad(GCController *p_controller) {
if (controller_to_joy_id.has(p_controller)) {
return;
}
// Get a new id for our controller.
int joy_id = Input::get_singleton()->get_unused_joy_id();
if (joy_id == -1) {
print_verbose("Couldn't retrieve new joy ID.");
return;
}
// Assign our player index.
if (p_controller.playerIndex == GCControllerPlayerIndexUnset) {
p_controller.playerIndex = get_free_player_index();
}
// Tell Godot about our new controller.
Input::get_singleton()->joy_connection_changed(joy_id, true, String::utf8(p_controller.vendorName.UTF8String));
// Assign our player index.
joypads.insert(joy_id, memnew(GameController(joy_id, p_controller)));
controller_to_joy_id.insert(p_controller, joy_id);
}
void JoypadApple::remove_joypad(GCController *p_controller) {
if (!controller_to_joy_id.has(p_controller)) {
return;
}
int joy_id = controller_to_joy_id[p_controller];
controller_to_joy_id.erase(p_controller);
// Tell Godot this joystick is no longer there.
Input::get_singleton()->joy_connection_changed(joy_id, false, "");
// And remove it from our dictionary.
GameController **old = joypads.getptr(joy_id);
memdelete(*old);
*old = nullptr;
joypads.erase(joy_id);
}
API_AVAILABLE(macos(10.15), ios(13.0), tvos(14.0))
CHHapticPattern *get_vibration_pattern(float p_magnitude, float p_duration) {
// Creates a vibration pattern with an intensity and duration.
NSDictionary *hapticDict = @{
CHHapticPatternKeyPattern : @[
@{
CHHapticPatternKeyEvent : @{
CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous,
CHHapticPatternKeyTime : @(CHHapticTimeImmediate),
CHHapticPatternKeyEventDuration : [NSNumber numberWithFloat:p_duration],
CHHapticPatternKeyEventParameters : @[
@{
CHHapticPatternKeyParameterID : CHHapticEventParameterIDHapticIntensity,
CHHapticPatternKeyParameterValue : [NSNumber numberWithFloat:p_magnitude]
},
],
},
},
],
};
NSError *error;
CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error];
return pattern;
}
void JoypadApple::joypad_vibration_start(GameController &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
if (!p_joypad.force_feedback || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
return;
}
// If there is active vibration players, stop them.
if (p_joypad.rumble_context->has_active_players()) {
joypad_vibration_stop(p_joypad, p_timestamp);
}
// Gets the default vibration pattern and creates a player for each motor.
CHHapticPattern *weak_pattern = get_vibration_pattern(p_weak_magnitude, p_duration);
CHHapticPattern *strong_pattern = get_vibration_pattern(p_strong_magnitude, p_duration);
p_joypad.rumble_context->play_weak_pattern(weak_pattern);
p_joypad.rumble_context->play_strong_pattern(strong_pattern);
p_joypad.ff_effect_timestamp = p_timestamp;
}
void JoypadApple::joypad_vibration_stop(GameController &p_joypad, uint64_t p_timestamp) {
if (!p_joypad.force_feedback) {
return;
}
// If there is no active vibration players, exit.
if (!p_joypad.rumble_context->has_active_players()) {
return;
}
p_joypad.rumble_context->stop();
p_joypad.ff_effect_timestamp = p_timestamp;
}
void JoypadApple::process_joypads() {
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
for (KeyValue<int, GameController *> &E : joypads) {
int id = E.key;
GameController &joypad = *E.value;
if (joypad.force_feedback) {
Input *input = Input::get_singleton();
uint64_t timestamp = input->get_joy_vibration_timestamp(id);
if (timestamp > (unsigned)joypad.ff_effect_timestamp) {
Vector2 strength = input->get_joy_vibration_strength(id);
float duration = input->get_joy_vibration_duration(id);
if (duration == 0) {
duration = GCHapticDurationInfinite;
}
if (strength.x == 0 && strength.y == 0) {
joypad_vibration_stop(joypad, timestamp);
} else {
joypad_vibration_start(joypad, strength.x, strength.y, duration, timestamp);
}
}
}
}
}
}

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
# Driver source files
env.add_source_files(env.drivers_sources, "*.cpp")
env.add_source_files(env.drivers_sources, "*.mm")

View file

@ -38,6 +38,8 @@
#import <AudioUnit/AudioUnit.h>
#ifdef MACOS_ENABLED
#import <CoreAudio/AudioHardware.h>
#else
#import <AVFoundation/AVFoundation.h>
#endif
class AudioDriverCoreAudio : public AudioDriver {
@ -51,9 +53,11 @@ class AudioDriverCoreAudio : public AudioDriver {
String input_device_name = "Default";
int mix_rate = 0;
int capture_mix_rate = 0;
unsigned int channels = 2;
unsigned int capture_channels = 2;
unsigned int buffer_frames = 0;
unsigned int capture_buffer_frames = 0;
Vector<int32_t> samples_in;
Vector<int16_t> input_buf;
@ -89,11 +93,12 @@ class AudioDriverCoreAudio : public AudioDriver {
public:
virtual const char *get_name() const override {
return "CoreAudio";
};
}
virtual Error init() override;
virtual void start() override;
virtual int get_mix_rate() const override;
virtual int get_input_mix_rate() const override;
virtual SpeakerMode get_speaker_mode() const override;
virtual void lock() override;

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* audio_driver_coreaudio.cpp */
/* audio_driver_coreaudio.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -66,6 +66,11 @@ OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID
return noErr;
}
// Switch to kAudioObjectPropertyElementMain everywhere to remove deprecated warnings.
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 120000) || (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED < 150000)
#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
#endif
#endif
Error AudioDriverCoreAudio::init() {
@ -89,7 +94,7 @@ Error AudioDriverCoreAudio::init() {
AudioObjectPropertyAddress prop;
prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
prop.mScope = kAudioObjectPropertyScopeGlobal;
prop.mElement = kAudioObjectPropertyElementMaster;
prop.mElement = kAudioObjectPropertyElementMain;
result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
ERR_FAIL_COND_V(result != noErr, FAILED);
@ -116,7 +121,24 @@ Error AudioDriverCoreAudio::init() {
break;
}
mix_rate = _get_configured_mix_rate();
#ifdef MACOS_ENABLED
AudioDeviceID device_id;
UInt32 dev_id_size = sizeof(AudioDeviceID);
AudioObjectPropertyAddress property_dev_id = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_dev_id, 0, nullptr, &dev_id_size, &device_id);
ERR_FAIL_COND_V(result != noErr, FAILED);
double hw_mix_rate;
UInt32 hw_mix_rate_size = sizeof(hw_mix_rate);
AudioObjectPropertyAddress property_sr = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
result = AudioObjectGetPropertyData(device_id, &property_sr, 0, nullptr, &hw_mix_rate_size, &hw_mix_rate);
ERR_FAIL_COND_V(result != noErr, FAILED);
#else
double hw_mix_rate = [AVAudioSession sharedInstance].sampleRate;
#endif
mix_rate = hw_mix_rate;
memset(&strdesc, 0, sizeof(strdesc));
strdesc.mFormatID = kAudioFormatLinearPCM;
@ -142,10 +164,10 @@ Error AudioDriverCoreAudio::init() {
unsigned int buffer_size = buffer_frames * channels;
samples_in.resize(buffer_size);
input_buf.resize(buffer_size);
print_verbose("CoreAudio: detected " + itos(channels) + " channels");
print_verbose("CoreAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
print_verbose("CoreAudio: output sampling rate: " + itos(mix_rate) + " Hz");
print_verbose("CoreAudio: output audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
AURenderCallbackStruct callback;
memset(&callback, 0, sizeof(AURenderCallbackStruct));
@ -245,7 +267,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon,
}
void AudioDriverCoreAudio::start() {
if (!active) {
if (!active && audio_unit != nullptr) {
OSStatus result = AudioOutputUnitStart(audio_unit);
if (result != noErr) {
ERR_PRINT("AudioOutputUnitStart failed, code: " + itos(result));
@ -270,6 +292,10 @@ int AudioDriverCoreAudio::get_mix_rate() const {
return mix_rate;
}
int AudioDriverCoreAudio::get_input_mix_rate() const {
return capture_mix_rate;
}
AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const {
return get_speaker_mode_by_total_channels(channels);
}
@ -319,7 +345,7 @@ void AudioDriverCoreAudio::finish() {
AudioObjectPropertyAddress prop;
prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
prop.mScope = kAudioObjectPropertyScopeGlobal;
prop.mElement = kAudioObjectPropertyElementMaster;
prop.mElement = kAudioObjectPropertyElementMain;
result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
if (result != noErr) {
@ -358,7 +384,7 @@ Error AudioDriverCoreAudio::init_input_device() {
AudioObjectPropertyAddress prop;
prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
prop.mScope = kAudioObjectPropertyScopeGlobal;
prop.mElement = kAudioObjectPropertyElementMaster;
prop.mElement = kAudioObjectPropertyElementMain;
result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this);
ERR_FAIL_COND_V(result != noErr, FAILED);
@ -373,14 +399,14 @@ Error AudioDriverCoreAudio::init_input_device() {
UInt32 size;
#ifdef MACOS_ENABLED
AudioDeviceID deviceId;
AudioDeviceID device_id;
size = sizeof(AudioDeviceID);
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId);
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &device_id);
ERR_FAIL_COND_V(result != noErr, FAILED);
result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
ERR_FAIL_COND_V(result != noErr, FAILED);
#endif
@ -405,13 +431,23 @@ Error AudioDriverCoreAudio::init_input_device() {
break;
}
mix_rate = _get_configured_mix_rate();
#ifdef MACOS_ENABLED
double hw_mix_rate;
UInt32 hw_mix_rate_size = sizeof(hw_mix_rate);
AudioObjectPropertyAddress property_sr = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
result = AudioObjectGetPropertyData(device_id, &property_sr, 0, nullptr, &hw_mix_rate_size, &hw_mix_rate);
ERR_FAIL_COND_V(result != noErr, FAILED);
#else
double hw_mix_rate = [AVAudioSession sharedInstance].sampleRate;
#endif
capture_mix_rate = hw_mix_rate;
memset(&strdesc, 0, sizeof(strdesc));
strdesc.mFormatID = kAudioFormatLinearPCM;
strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
strdesc.mChannelsPerFrame = capture_channels;
strdesc.mSampleRate = mix_rate;
strdesc.mSampleRate = capture_mix_rate;
strdesc.mFramesPerPacket = 1;
strdesc.mBitsPerChannel = 16;
strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
@ -420,6 +456,13 @@ Error AudioDriverCoreAudio::init_input_device() {
result = AudioUnitSetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc));
ERR_FAIL_COND_V(result != noErr, FAILED);
int latency = Engine::get_singleton()->get_audio_output_latency();
// Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
capture_buffer_frames = closest_power_of_2(latency * capture_mix_rate / 1000);
unsigned int buffer_size = capture_buffer_frames * capture_channels;
input_buf.resize(buffer_size);
AURenderCallbackStruct callback;
memset(&callback, 0, sizeof(AURenderCallbackStruct));
callback.inputProc = &AudioDriverCoreAudio::input_callback;
@ -430,6 +473,9 @@ Error AudioDriverCoreAudio::init_input_device() {
result = AudioUnitInitialize(input_unit);
ERR_FAIL_COND_V(result != noErr, FAILED);
print_verbose("CoreAudio: input sampling rate: " + itos(capture_mix_rate) + " Hz");
print_verbose("CoreAudio: input audio buffer frames: " + itos(capture_buffer_frames) + " calculated latency: " + itos(capture_buffer_frames * 1000 / capture_mix_rate) + "ms");
return OK;
}
@ -453,7 +499,7 @@ void AudioDriverCoreAudio::finish_input_device() {
AudioObjectPropertyAddress prop;
prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
prop.mScope = kAudioObjectPropertyScopeGlobal;
prop.mElement = kAudioObjectPropertyElementMaster;
prop.mElement = kAudioObjectPropertyElementMain;
result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this);
if (result != noErr) {
@ -472,7 +518,7 @@ void AudioDriverCoreAudio::finish_input_device() {
}
Error AudioDriverCoreAudio::input_start() {
input_buffer_init(buffer_frames);
input_buffer_init(capture_buffer_frames);
OSStatus result = AudioOutputUnitStart(input_unit);
if (result != noErr) {
@ -504,7 +550,7 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool input) {
prop.mSelector = kAudioHardwarePropertyDevices;
prop.mScope = kAudioObjectPropertyScopeGlobal;
prop.mElement = kAudioObjectPropertyElementMaster;
prop.mElement = kAudioObjectPropertyElementMain;
UInt32 size = 0;
AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size);
@ -556,14 +602,14 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool input) {
}
void AudioDriverCoreAudio::_set_device(const String &output_device, bool input) {
AudioDeviceID deviceId;
AudioDeviceID device_id;
bool found = false;
if (output_device != "Default") {
AudioObjectPropertyAddress prop;
prop.mSelector = kAudioHardwarePropertyDevices;
prop.mScope = kAudioObjectPropertyScopeGlobal;
prop.mElement = kAudioObjectPropertyElementMaster;
prop.mElement = kAudioObjectPropertyElementMain;
UInt32 size = 0;
AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size);
@ -603,7 +649,7 @@ void AudioDriverCoreAudio::_set_device(const String &output_device, bool input)
if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")";
if (name == output_device) {
deviceId = audioDevices[i];
device_id = audioDevices[i];
found = true;
}
}
@ -619,16 +665,16 @@ void AudioDriverCoreAudio::_set_device(const String &output_device, bool input)
// 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 };
AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId);
OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &device_id);
ERR_FAIL_COND(result != noErr);
found = true;
}
if (found) {
OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
ERR_FAIL_COND(result != noErr);
if (input) {

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -1,8 +1,11 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
import os
from pathlib import Path
import methods
Import("env")
env_d3d12_rdd = env.Clone()
@ -136,9 +139,12 @@ if env.msvc:
]
else:
extra_defines += [
("__REQUIRED_RPCNDR_H_VERSION__", 475),
"HAVE_STRUCT_TIMESPEC",
]
if methods.using_gcc(env) and methods.get_compiler_version(env)["major"] < 13:
# `region` & `endregion` not recognized as valid pragmas.
env_d3d12_rdd.Append(CCFLAGS=["-Wno-unknown-pragmas"])
env.Append(CCFLAGS=["-Wno-unknown-pragmas"])
# This is needed since rendering_device_d3d12.cpp needs to include some Mesa internals.
env_d3d12_rdd.Prepend(CPPPATH=mesa_private_inc_paths)

View file

@ -58,7 +58,7 @@
#endif
#if defined(_MSC_VER)
#pragma warning(disable : 4189 4324 4505)
#pragma warning(disable : 4189 4505)
#endif
#include "thirdparty/d3d12ma/D3D12MemAlloc.cpp"

View file

@ -96,7 +96,7 @@ void compute_dxil_hash(const BYTE *pData, UINT byteCount, BYTE *pOutHash) {
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];
UINT x[16] = {};
const UINT *pX;
if (i == NextEndState) {
if (!bTwoRowsPadding && i == N - 1) {

View file

@ -96,6 +96,9 @@ RenderingContextDriverD3D12::~RenderingContextDriverD3D12() {
if (lib_dxgi) {
FreeLibrary(lib_dxgi);
}
if (lib_dcomp) {
FreeLibrary(lib_dcomp);
}
}
Error RenderingContextDriverD3D12::_init_device_factory() {
@ -108,6 +111,9 @@ Error RenderingContextDriverD3D12::_init_device_factory() {
lib_dxgi = LoadLibraryW(L"DXGI.dll");
ERR_FAIL_NULL_V(lib_dxgi, ERR_CANT_CREATE);
lib_dcomp = LoadLibraryW(L"Dcomp.dll");
ERR_FAIL_NULL_V(lib_dcomp, ERR_CANT_CREATE);
// Note: symbol is not available in MinGW import library.
PFN_D3D12_GET_INTERFACE d3d_D3D12GetInterface = (PFN_D3D12_GET_INTERFACE)(void *)GetProcAddress(lib_d3d12, "D3D12GetInterface");
if (!d3d_D3D12GetInterface) {
@ -178,7 +184,7 @@ Error RenderingContextDriverD3D12::_initialize_devices() {
Device &device = driver_devices[i];
device.name = desc.Description;
device.vendor = Vendor(desc.VendorId);
device.vendor = desc.VendorId;
device.workarounds = Workarounds();
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {

View file

@ -59,6 +59,20 @@
#undef AS
#endif
#if (WINVER < _WIN32_WINNT_WIN8) && defined(_MSC_VER)
#pragma push_macro("NTDDI_VERSION")
#pragma push_macro("WINVER")
#undef NTDDI_VERSION
#undef WINVER
#define NTDDI_VERSION NTDDI_WIN8
#define WINVER _WIN32_WINNT_WIN8
#include <dcomp.h>
#pragma pop_macro("WINVER")
#pragma pop_macro("NTDDI_VERSION")
#else
#include <dcomp.h>
#endif
#include "d3dx12.h"
#include <dxgi1_6.h>
@ -114,10 +128,14 @@ public:
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
bool needs_resize = false;
ComPtr<IDCompositionDevice> composition_device;
ComPtr<IDCompositionTarget> composition_target;
ComPtr<IDCompositionVisual> composition_visual;
};
HMODULE lib_d3d12 = nullptr;
HMODULE lib_dxgi = nullptr;
HMODULE lib_dcomp = nullptr;
IDXGIAdapter1 *create_adapter(uint32_t p_adapter_index) const;
ID3D12DeviceFactory *device_factory_get() const;

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,11 @@
#include "core/templates/self_list.h"
#include "servers/rendering/rendering_device_driver.h"
#ifndef _MSC_VER
// Match current version used by MinGW, MSVC and Direct3D 12 headers use 500.
#define __REQUIRED_RPCNDR_H_VERSION__ 475
#endif
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
@ -75,7 +80,6 @@ using Microsoft::WRL::ComPtr;
#define D3D12_BITCODE_OFFSETS_NUM_STAGES 3
#ifdef DEV_ENABLED
//#define DEBUG_COUNT_BARRIERS
#define CUSTOM_INFO_QUEUE_ENABLED 0
#endif
@ -218,20 +222,6 @@ private:
ComPtr<D3D12MA::Allocator> allocator;
#define USE_SMALL_ALLOCS_POOL // Disabled by now; seems not to be beneficial as it is in Vulkan.
#ifdef USE_SMALL_ALLOCS_POOL
union AllocPoolKey {
struct {
D3D12_HEAP_TYPE heap_type;
D3D12_HEAP_FLAGS heap_flags;
};
uint64_t key = 0;
};
HashMap<uint64_t, ComPtr<D3D12MA::Pool>> small_allocs_pools;
D3D12MA::Pool *_find_or_create_small_allocs_pool(D3D12_HEAP_TYPE p_heap_type, D3D12_HEAP_FLAGS p_heap_flags);
#endif
/******************/
/**** RESOURCE ****/
/******************/
@ -269,20 +259,11 @@ private:
uint8_t groups_count = 0;
static const D3D12_RESOURCE_STATES DELETED_GROUP = D3D12_RESOURCE_STATES(0xFFFFFFFFU);
};
PagedAllocator<HashMapElement<ResourceInfo::States *, BarrierRequest>> res_barriers_requests_allocator;
HashMap<ResourceInfo::States *, BarrierRequest, HashMapHasherDefault, HashMapComparatorDefault<ResourceInfo::States *>, decltype(res_barriers_requests_allocator)> res_barriers_requests;
LocalVector<D3D12_RESOURCE_BARRIER> res_barriers;
uint32_t res_barriers_count = 0;
uint32_t res_barriers_batch = 0;
#ifdef DEBUG_COUNT_BARRIERS
int frame_barriers_count = 0;
int frame_barriers_batches_count = 0;
uint64_t frame_barriers_cpu_time = 0;
#endif
struct CommandBufferInfo;
void _resource_transition_batch(ResourceInfo *p_resource, uint32_t p_subresource, uint32_t p_num_planes, D3D12_RESOURCE_STATES p_new_state);
void _resource_transitions_flush(ID3D12GraphicsCommandList *p_cmd_list);
void _resource_transition_batch(CommandBufferInfo *p_command_buffer, ResourceInfo *p_resource, uint32_t p_subresource, uint32_t p_num_planes, D3D12_RESOURCE_STATES p_new_state);
void _resource_transitions_flush(CommandBufferInfo *p_command_buffer);
/*****************/
/**** BUFFERS ****/
@ -303,6 +284,7 @@ public:
virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final;
virtual uint8_t *buffer_map(BufferID p_buffer) override final;
virtual void buffer_unmap(BufferID p_buffer) override final;
virtual uint64_t buffer_get_device_address(BufferID p_buffer) override final;
/*****************/
/**** TEXTURE ****/
@ -329,6 +311,7 @@ private:
SelfList<TextureInfo>::List textures_pending_clear;
HashMap<DXGI_FORMAT, uint32_t> format_sample_counts_mask_cache;
Mutex format_sample_counts_mask_cache_mutex;
uint32_t _find_max_common_supported_sample_count(VectorView<DXGI_FORMAT> p_formats);
UINT _compute_component_mapping(const TextureView &p_view);
@ -336,7 +319,6 @@ private:
UINT _compute_plane_slice(DataFormat p_format, TextureAspect p_aspect);
UINT _compute_subresource_from_layers(TextureInfo *p_texture, const TextureSubresourceLayers &p_layers, uint32_t p_layer_offset);
struct CommandBufferInfo;
void _discard_texture_subresources(const TextureInfo *p_tex_info, const CommandBufferInfo *p_cmd_buf_info);
protected:
@ -449,10 +431,14 @@ private:
struct CommandPoolInfo {
CommandQueueFamilyID queue_family;
CommandBufferType buffer_type = COMMAND_BUFFER_TYPE_PRIMARY;
// Since there are no command pools in D3D12, we need to track the command buffers created by this pool
// so that we can free them when the pool is freed.
SelfList<CommandBufferInfo>::List command_buffers;
};
public:
virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override final;
virtual bool command_pool_reset(CommandPoolID p_cmd_pool) override final;
virtual void command_pool_free(CommandPoolID p_cmd_pool) override final;
// ----- BUFFER -----
@ -476,6 +462,9 @@ private:
// Leveraging knowledge of actual usage and D3D12 specifics (namely, command lists from the same allocator
// can't be freely begun and ended), an allocator per list works better.
struct CommandBufferInfo {
// Store a self list reference to be used by the command pool.
SelfList<CommandBufferInfo> command_buffer_info_elem{ this };
ComPtr<ID3D12CommandAllocator> cmd_allocator;
ComPtr<ID3D12GraphicsCommandList> cmd_list;
@ -487,6 +476,11 @@ private:
RenderPassState render_pass_state;
bool descriptor_heaps_set = false;
HashMap<ResourceInfo::States *, BarrierRequest> res_barriers_requests;
LocalVector<D3D12_RESOURCE_BARRIER> res_barriers;
uint32_t res_barriers_count = 0;
uint32_t res_barriers_batch = 0;
};
public:
@ -711,9 +705,10 @@ private:
public:
virtual String shader_get_binary_cache_key() override final;
virtual Vector<uint8_t> shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) override final;
virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name) override final;
virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name, const Vector<ImmutableSampler> &p_immutable_samplers) override final;
virtual uint32_t shader_get_layout_hash(ShaderID p_shader) override final;
virtual void shader_free(ShaderID p_shader) override final;
virtual void shader_destroy_modules(ShaderID p_shader) override final;
/*********************/
/**** UNIFORM SET ****/
@ -760,7 +755,7 @@ private:
};
public:
virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index) override final;
virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index, int p_linear_pool_index) override final;
virtual void uniform_set_free(UniformSetID p_uniform_set) override final;
// ----- COMMANDS -----
@ -770,6 +765,7 @@ public:
private:
void _command_check_descriptor_sets(CommandBufferID p_cmd_buffer);
void _command_bind_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index, bool p_for_compute);
void _command_bind_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count, bool p_for_compute);
public:
/******************/
@ -791,10 +787,25 @@ public:
/**** PIPELINE ****/
/******************/
virtual void pipeline_free(PipelineID p_pipeline) override final;
struct RenderPipelineInfo {
const VertexFormatInfo *vf_info = nullptr;
private:
HashMap<ID3D12PipelineState *, const ShaderInfo *> pipelines_shaders;
struct {
D3D12_PRIMITIVE_TOPOLOGY primitive_topology = {};
Color blend_constant;
float depth_bounds_min = 0.0f;
float depth_bounds_max = 0.0f;
uint32_t stencil_reference = 0;
} dyn_params;
};
struct PipelineInfo {
ID3D12PipelineState *pso = nullptr;
const ShaderInfo *shader_info = nullptr;
RenderPipelineInfo render_info;
};
virtual void pipeline_free(PipelineID p_pipeline) override final;
public:
// ----- BINDING -----
@ -844,6 +855,7 @@ public:
// Binding.
virtual void command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
virtual void command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
virtual void command_bind_render_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count) override final;
// Drawing.
virtual void command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) override final;
@ -867,20 +879,6 @@ public:
// ----- PIPELINE -----
private:
struct RenderPipelineExtraInfo {
struct {
D3D12_PRIMITIVE_TOPOLOGY primitive_topology = {};
Color blend_constant;
float depth_bounds_min = 0.0f;
float depth_bounds_max = 0.0f;
uint32_t stencil_reference = 0;
} dyn_params;
const VertexFormatInfo *vf_info = nullptr;
};
HashMap<ID3D12PipelineState *, RenderPipelineExtraInfo> render_psos_extra_info;
public:
virtual PipelineID render_pipeline_create(
ShaderID p_shader,
@ -905,6 +903,7 @@ public:
// Binding.
virtual void command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
virtual void command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
virtual void command_bind_compute_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count) override final;
// Dispatching.
virtual void command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final;
@ -945,6 +944,11 @@ public:
virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final;
virtual void command_end_label(CommandBufferID p_cmd_buffer) override final;
/****************/
/**** DEBUG *****/
/****************/
virtual void command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) override final;
/********************/
/**** SUBMISSION ****/
/********************/
@ -993,6 +997,7 @@ public:
virtual void set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) override final;
virtual uint64_t get_resource_native_handle(DriverResource p_type, ID p_driver_id) override final;
virtual uint64_t get_total_memory_used() override final;
virtual uint64_t get_lazily_memory_used() override final;
virtual uint64_t limit_get(Limit p_limit) override final;
virtual uint64_t api_trait_get(ApiTrait p_trait) override final;
virtual bool has_feature(Features p_feature) override final;
@ -1023,7 +1028,7 @@ private:
UniformSetInfo,
RenderPassInfo,
TimestampQueryPoolInfo>;
PagedAllocator<VersatileResource> resources_allocator;
PagedAllocator<VersatileResource, true> resources_allocator;
/******************/

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -30,6 +30,10 @@
#include "egl_manager.h"
#include "core/crypto/crypto_core.h"
#include "core/io/dir_access.h"
#include "drivers/gles3/rasterizer_gles3.h"
#ifdef EGL_ENABLED
#if defined(EGL_STATIC)
@ -51,6 +55,16 @@ extern "C" EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplayEXT(EGLenum platfo
#define GLAD_EGL_EXT_platform_base 0
#endif
#ifdef WINDOWS_ENABLED
// Unofficial ANGLE extension: EGL_ANGLE_surface_orientation
#ifndef EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE
#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
#define EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001
#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002
#endif
#endif
// Creates and caches a GLDisplay. Returns -1 on error.
int EGLManager::_get_gldisplay_id(void *p_display) {
// Look for a cached GLDisplay.
@ -115,6 +129,18 @@ int EGLManager::_get_gldisplay_id(void *p_display) {
}
#endif
#ifdef WINDOWS_ENABLED
String client_extensions_string = eglQueryString(new_gldisplay.egl_display, EGL_EXTENSIONS);
if (eglGetError() == EGL_SUCCESS) {
Vector<String> egl_extensions = client_extensions_string.split(" ");
if (egl_extensions.has("EGL_ANGLE_surface_orientation")) {
new_gldisplay.has_EGL_ANGLE_surface_orientation = true;
print_verbose("EGL: EGL_ANGLE_surface_orientation is supported.");
}
}
#endif
displays.push_back(new_gldisplay);
// Return the new GLDisplay's ID.
@ -237,8 +263,29 @@ Error EGLManager::window_create(DisplayServer::WindowID p_window_id, void *p_dis
GLWindow &glwindow = windows[p_window_id];
glwindow.gldisplay_id = gldisplay_id;
Vector<EGLAttrib> egl_attribs;
#ifdef WINDOWS_ENABLED
if (gldisplay.has_EGL_ANGLE_surface_orientation) {
EGLint optimal_orientation;
if (eglGetConfigAttrib(gldisplay.egl_display, gldisplay.egl_config, EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, &optimal_orientation)) {
// We only need to support inverting Y for optimizing ANGLE on D3D11.
if (optimal_orientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE && !(optimal_orientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE)) {
egl_attribs.push_back(EGL_SURFACE_ORIENTATION_ANGLE);
egl_attribs.push_back(EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE);
}
} else {
ERR_PRINT(vformat("Failed to get EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, error: 0x%08X", eglGetError()));
}
}
if (!egl_attribs.is_empty()) {
egl_attribs.push_back(EGL_NONE);
}
#endif
if (GLAD_EGL_VERSION_1_5) {
glwindow.egl_surface = eglCreatePlatformWindowSurface(gldisplay.egl_display, gldisplay.egl_config, p_native_window, nullptr);
glwindow.egl_surface = eglCreatePlatformWindowSurface(gldisplay.egl_display, gldisplay.egl_config, p_native_window, egl_attribs.ptr());
} else {
EGLNativeWindowType *native_window_type = (EGLNativeWindowType *)p_native_window;
glwindow.egl_surface = eglCreateWindowSurface(gldisplay.egl_display, gldisplay.egl_config, *native_window_type, nullptr);
@ -250,6 +297,20 @@ Error EGLManager::window_create(DisplayServer::WindowID p_window_id, void *p_dis
glwindow.initialized = true;
#ifdef WINDOWS_ENABLED
if (gldisplay.has_EGL_ANGLE_surface_orientation) {
EGLint orientation;
if (eglQuerySurface(gldisplay.egl_display, glwindow.egl_surface, EGL_SURFACE_ORIENTATION_ANGLE, &orientation)) {
if (orientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE && !(orientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE)) {
glwindow.flipped_y = true;
print_verbose("EGL: Using optimal surface orientation: Invert Y");
}
} else {
ERR_PRINT(vformat("Failed to get EGL_SURFACE_ORIENTATION_ANGLE, error: 0x%08X", eglGetError()));
}
}
#endif
window_make_current(p_window_id);
return OK;
@ -316,6 +377,10 @@ void EGLManager::window_make_current(DisplayServer::WindowID p_window_id) {
GLDisplay &current_display = displays[current_window->gldisplay_id];
eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
#ifdef WINDOWS_ENABLED
RasterizerGLES3::set_screen_flipped_y(glwindow.flipped_y);
#endif
}
void EGLManager::set_use_vsync(bool p_use) {
@ -351,13 +416,37 @@ EGLContext EGLManager::get_context(DisplayServer::WindowID p_window_id) {
return display.egl_context;
}
EGLDisplay EGLManager::get_display(DisplayServer::WindowID p_window_id) {
GLWindow &glwindow = windows[p_window_id];
if (!glwindow.initialized) {
return EGL_NO_CONTEXT;
}
GLDisplay &display = displays[glwindow.gldisplay_id];
return display.egl_display;
}
EGLConfig EGLManager::get_config(DisplayServer::WindowID p_window_id) {
GLWindow &glwindow = windows[p_window_id];
if (!glwindow.initialized) {
return nullptr;
}
GLDisplay &display = displays[glwindow.gldisplay_id];
return display.egl_config;
}
Error EGLManager::initialize(void *p_native_display) {
#if defined(GLAD_ENABLED) && !defined(EGL_STATIC)
// Loading EGL with a new display gets us just the bare minimum API. We'll then
// 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.");
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't load EGL dynamic library.");
}
EGLDisplay tmp_display = EGL_NO_DISPLAY;
@ -387,7 +476,7 @@ Error EGLManager::initialize(void *p_native_display) {
int version = gladLoaderLoadEGL(tmp_display);
if (!version) {
eglTerminate(tmp_display);
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't load EGL.");
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't load EGL dynamic library.");
}
int major = GLAD_VERSION_MAJOR(version);

View file

@ -36,10 +36,6 @@
// 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"
@ -53,11 +49,18 @@ private:
EGLDisplay egl_display = EGL_NO_DISPLAY;
EGLContext egl_context = EGL_NO_CONTEXT;
EGLConfig egl_config = nullptr;
#ifdef WINDOWS_ENABLED
bool has_EGL_ANGLE_surface_orientation = false;
#endif
};
// EGL specific window data.
struct GLWindow {
bool initialized = false;
#ifdef WINDOWS_ENABLED
bool flipped_y = false;
#endif
// An handle to the GLDisplay associated with this window.
int gldisplay_id = -1;
@ -106,6 +109,8 @@ public:
bool is_using_vsync() const;
EGLContext get_context(DisplayServer::WindowID p_window_id);
EGLDisplay get_display(DisplayServer::WindowID p_window_id);
EGLConfig get_config(DisplayServer::WindowID p_window_id);
Error initialize(void *p_native_display = nullptr);

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -41,7 +41,10 @@ CubemapFilter *CubemapFilter::singleton = nullptr;
CubemapFilter::CubemapFilter() {
singleton = this;
ggx_samples = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
// Use a factor 4 larger for the compatibility renderer to make up for the fact
// That we don't use an array texture. We will reduce samples on low roughness
// to compensate.
ggx_samples = 4 * uint32_t(GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples"));
{
String defines;
@ -57,10 +60,10 @@ CubemapFilter::CubemapFilter() {
const float qv[6] = {
-1.0f,
-1.0f,
3.0f,
-1.0f,
-1.0f,
3.0f,
3.0f,
-1.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
@ -146,10 +149,11 @@ void CubemapFilter::filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubem
}
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)];
const uint32_t sample_counts[5] = { 1, ggx_samples / 16, ggx_samples / 8, ggx_samples / 4, ggx_samples };
uint32_t sample_count = sample_counts[MIN(4, p_layer)];
float roughness = float(p_layer) / (p_mipmap_count);
float roughness = float(p_layer) / (p_mipmap_count - 1);
roughness *= roughness; // Convert to non-perceptual roughness.
float roughness4 = roughness * roughness;
roughness4 *= roughness4;
@ -165,7 +169,7 @@ void CubemapFilter::filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubem
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) {
if (light_vec.z <= 0.0) {
continue;
}

View file

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

View file

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

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -33,9 +33,6 @@
#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 {

View file

@ -33,13 +33,8 @@
#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 {

View file

@ -32,7 +32,6 @@
#ifdef GLES3_ENABLED
#include "core/os/os.h"
#include "rasterizer_gles3.h"
#include "rasterizer_scene_gles3.h"
@ -468,7 +467,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
update_skeletons = false;
}
// Canvas group begins here, render until before this item
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info);
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info, material_screen_texture_mipmaps_cached);
item_count = 0;
if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) {
@ -499,7 +498,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
mesh_storage->update_mesh_instances();
update_skeletons = false;
}
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info);
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info, material_screen_texture_mipmaps_cached);
item_count = 0;
if (ci->canvas_group->blur_mipmaps) {
@ -523,7 +522,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
}
//render anything pending, including clearing if no items
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info);
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info, material_screen_texture_mipmaps_cached);
item_count = 0;
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
@ -553,7 +552,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
mesh_storage->update_mesh_instances();
update_skeletons = false;
}
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info);
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info, material_screen_texture_mipmaps_cached);
//then reset
item_count = 0;
}
@ -573,10 +572,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
state.current_instance_buffer_index = 0;
}
void RasterizerCanvasGLES3::_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, RenderingMethod::RenderInfo *r_render_info) {
void RasterizerCanvasGLES3::_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, RenderingMethod::RenderInfo *r_render_info, bool p_backbuffer_has_mipmaps) {
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
canvas_begin(p_to_render_target, p_to_backbuffer);
canvas_begin(p_to_render_target, p_to_backbuffer, p_backbuffer_has_mipmaps);
if (p_item_count <= 0) {
// Nothing to draw, just call canvas_begin() to clear the render target and return.
@ -647,25 +646,22 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, Point2());
} else {
Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2);
Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos;
Point2 pos = start_pos;
Point2 offset;
do {
do {
_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, pos);
pos.y += ci->repeat_size.y;
} while (pos.y < end_pos.y);
pos.x += ci->repeat_size.x;
pos.y = start_pos.y;
} while (pos.x < end_pos.x);
int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0;
int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0;
for (int ry = 0; ry <= repeat_times_y; ry++) {
offset.y = start_pos.y + ry * ci->repeat_size.y;
for (int rx = 0; rx <= repeat_times_x; rx++) {
offset.x = start_pos.x + rx * ci->repeat_size.x;
_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, offset);
}
}
}
}
if (index == 0) {
// Nothing to render, just return.
state.current_batch_index = 0;
state.canvas_instance_batches.clear();
return;
}
@ -688,6 +684,8 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
state.current_tex = RID();
const uint64_t base_specialization = GLES3::Config::get_singleton()->float_texture_supported ? 0 : CanvasShaderGLES3::USE_RGBA_SHADOWS;
for (uint32_t i = 0; i <= state.current_batch_index; i++) {
// Skipping when there is no instances.
if (state.canvas_instance_batches[i].instance_count == 0) {
@ -706,10 +704,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
GLES3::CanvasMaterialData *material_data = state.canvas_instance_batches[i].material_data;
CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant;
uint64_t specialization = 0;
specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled);
specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1;
CanvasShaderGLES3::ShaderVariant variant = CanvasShaderGLES3::MODE_DEFAULT;
uint64_t specialization = state.canvas_instance_batches[i].specialization;
specialization |= base_specialization;
RID shader_version = data.canvas_shader_default_version;
if (material_data) {
@ -720,11 +717,15 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
}
bool success = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(shader_version, variant, specialization);
bool success = material_storage->shaders.canvas_shader.version_bind_shader(shader_version, variant, specialization);
if (!success) {
continue;
}
// Bind per-batch uniforms.
material_storage->shaders.canvas_shader.version_set_uniform(CanvasShaderGLES3::BATCH_FLAGS, state.canvas_instance_batches[i].flags, shader_version, variant, specialization);
material_storage->shaders.canvas_shader.version_set_uniform(CanvasShaderGLES3::SPECULAR_SHININESS_IN, state.canvas_instance_batches[i].specular_shininess, shader_version, variant, specialization);
GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode;
Color blend_color = state.canvas_instance_batches[i].blend_color;
@ -809,8 +810,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
state.last_item_index += index;
}
void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_offset) {
void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_repeat_offset) {
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter;
const uint64_t specialization_command_mask = ~(CanvasShaderGLES3::USE_NINEPATCH | CanvasShaderGLES3::USE_PRIMITIVE | CanvasShaderGLES3::USE_ATTRIBUTES | CanvasShaderGLES3::USE_INSTANCING);
if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) {
_new_batch(r_batch_broken);
@ -826,11 +828,11 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat;
}
Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
if (p_offset.x || p_offset.y) {
base_transform *= Transform2D(0, p_offset / p_item->xform_curr.get_scale()); // TODO: Interpolate or explain why not needed.
Transform2D base_transform = p_item->final_transform;
if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) {
base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset);
}
base_transform = p_canvas_transform_inverse * base_transform;
Transform2D draw_transform; // Used by transform command
@ -846,15 +848,20 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
uint32_t lights[4] = { 0, 0, 0, 0 };
uint16_t light_count = 0;
uint16_t shadow_mask = 0;
{
Light *light = p_lights;
while (light) {
if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) {
if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects(light->rect_cache)) {
uint32_t light_index = light->render_index_cache;
lights[light_count >> 2] |= light_index << ((light_count & 3) * 8);
if (p_item->light_mask & light->item_shadow_mask) {
shadow_mask |= 1 << light_count;
}
light_count++;
if (light_count == data.max_lights_per_item - 1) {
@ -864,14 +871,15 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
light = light->next_ptr;
}
base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT;
base_flags |= light_count << INSTANCE_FLAGS_LIGHT_COUNT_SHIFT;
base_flags |= shadow_mask << INSTANCE_FLAGS_SHADOW_MASKED_SHIFT;
}
bool lights_disabled = light_count == 0 && !state.using_directional_lights;
if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) {
if (lights_disabled != bool(state.canvas_instance_batches[state.current_batch_index].specialization & CanvasShaderGLES3::DISABLE_LIGHTING)) {
_new_batch(r_batch_broken);
state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled;
state.canvas_instance_batches[state.current_batch_index].specialization ^= CanvasShaderGLES3::DISABLE_LIGHTING;
}
const Item::Command *c = p_item->commands;
@ -897,15 +905,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0;
state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0;
state.instance_data_array[r_index].pad[0] = 0.0;
state.instance_data_array[r_index].pad[1] = 0.0;
state.instance_data_array[r_index].lights[0] = lights[0];
state.instance_data_array[r_index].lights[1] = lights[1];
state.instance_data_array[r_index].lights[2] = lights[2];
state.instance_data_array[r_index].lights[3] = lights[3];
state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); // Reset on each command for safety, keep canvastexture binding config.
state.instance_data_array[r_index].flags = base_flags;
state.instance_data_array[r_index].instance_uniforms_ofs = p_item->instance_allocated_shader_uniforms_offset;
Color blend_color = base_color;
GLES3::CanvasShaderData::BlendMode blend_mode = p_blend_mode;
@ -937,7 +943,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].tex = rect->texture;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT;
state.canvas_instance_batches[state.current_batch_index].command = c;
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_QUAD;
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
state.canvas_instance_batches[state.current_batch_index].flags = 0;
}
_prepare_canvas_texture(rect->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@ -960,20 +967,18 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
if (rect->flags & CANVAS_RECT_FLIP_H) {
src_rect.size.x *= -1;
state.instance_data_array[r_index].flags |= FLAGS_FLIP_H;
}
if (rect->flags & CANVAS_RECT_FLIP_V) {
src_rect.size.y *= -1;
state.instance_data_array[r_index].flags |= FLAGS_FLIP_V;
}
if (rect->flags & CANVAS_RECT_TRANSPOSE) {
state.instance_data_array[r_index].flags |= FLAGS_TRANSPOSE_RECT;
state.instance_data_array[r_index].flags |= INSTANCE_FLAGS_TRANSPOSE_RECT;
}
if (rect->flags & CANVAS_RECT_CLIP_UV) {
state.instance_data_array[r_index].flags |= FLAGS_CLIP_RECT_UV;
state.instance_data_array[r_index].flags |= INSTANCE_FLAGS_CLIP_RECT_UV;
}
} else {
@ -992,13 +997,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
}
if (rect->flags & CANVAS_RECT_MSDF) {
state.instance_data_array[r_index].flags |= FLAGS_USE_MSDF;
state.instance_data_array[r_index].flags |= INSTANCE_FLAGS_USE_MSDF;
state.instance_data_array[r_index].msdf[0] = rect->px_range; // Pixel range.
state.instance_data_array[r_index].msdf[1] = rect->outline; // Outline size.
state.instance_data_array[r_index].msdf[2] = 0.f; // Reserved.
state.instance_data_array[r_index].msdf[3] = 0.f; // Reserved.
} else if (rect->flags & CANVAS_RECT_LCD) {
state.instance_data_array[r_index].flags |= FLAGS_USE_LCD;
state.instance_data_array[r_index].flags |= INSTANCE_FLAGS_USE_LCD;
}
state.instance_data_array[r_index].modulation[0] = rect->modulate.r * base_color.r;
@ -1027,7 +1032,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].tex = np->texture;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH;
state.canvas_instance_batches[state.current_batch_index].command = c;
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_NINEPATCH;
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_NINEPATCH;
state.canvas_instance_batches[state.current_batch_index].flags = 0;
}
_prepare_canvas_texture(np->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@ -1065,11 +1072,11 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.instance_data_array[r_index].dst_rect[2] = dst_rect.size.width;
state.instance_data_array[r_index].dst_rect[3] = dst_rect.size.height;
state.instance_data_array[r_index].flags |= int(np->axis_x) << FLAGS_NINEPATCH_H_MODE_SHIFT;
state.instance_data_array[r_index].flags |= int(np->axis_y) << FLAGS_NINEPATCH_V_MODE_SHIFT;
state.instance_data_array[r_index].flags |= int(np->axis_x) << INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT;
state.instance_data_array[r_index].flags |= int(np->axis_y) << INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT;
if (np->draw_center) {
state.instance_data_array[r_index].flags |= FLAGS_NINEPACH_DRAW_CENTER;
state.instance_data_array[r_index].flags |= INSTANCE_FLAGS_NINEPACH_DRAW_CENTER;
}
state.instance_data_array[r_index].ninepatch_margins[0] = np->margin[SIDE_LEFT];
@ -1093,7 +1100,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON;
state.canvas_instance_batches[state.current_batch_index].command = c;
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES;
state.canvas_instance_batches[state.current_batch_index].flags = 0;
_prepare_canvas_texture(polygon->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@ -1120,7 +1129,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE;
state.canvas_instance_batches[state.current_batch_index].command = c;
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_PRIMITIVE;
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_PRIMITIVE;
state.canvas_instance_batches[state.current_batch_index].flags = 0;
}
_prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@ -1161,11 +1172,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
case Item::Command::TYPE_MESH:
case Item::Command::TYPE_MULTIMESH:
case Item::Command::TYPE_PARTICLES: {
// Mesh's can't be batched, so always create a new batch
// Meshes can't be batched, so always create a new batch.
_new_batch(r_batch_broken);
Color modulate(1, 1, 1, 1);
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES;
state.canvas_instance_batches[state.current_batch_index].flags = 0;
if (c->type == Item::Command::TYPE_MESH) {
const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
state.canvas_instance_batches[state.current_batch_index].tex = m->texture;
@ -1175,13 +1188,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
} else if (c->type == Item::Command::TYPE_MULTIMESH) {
const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c);
state.canvas_instance_batches[state.current_batch_index].tex = mm->texture;
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED;
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING;
if (GLES3::MeshStorage::get_singleton()->multimesh_uses_colors(mm->multimesh)) {
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_INSTANCING_HAS_COLORS;
}
if (GLES3::MeshStorage::get_singleton()->multimesh_uses_custom_data(mm->multimesh)) {
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA;
}
} else if (c->type == Item::Command::TYPE_PARTICLES) {
GLES3::ParticlesStorage *particles_storage = GLES3::ParticlesStorage::get_singleton();
@ -1190,9 +1203,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c);
RID particles = pt->particles;
state.canvas_instance_batches[state.current_batch_index].tex = pt->texture;
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED;
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING;
state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_INSTANCING_HAS_COLORS;
state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA;
if (particles_storage->particles_has_collision(particles) && texture_storage->render_target_is_sdf_enabled(p_render_target)) {
// Pass collision information.
@ -1554,7 +1567,9 @@ void RasterizerCanvasGLES3::_add_to_batch(uint32_t &r_index, bool &r_batch_broke
void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken) {
if (state.canvas_instance_batches.size() == 0) {
state.canvas_instance_batches.push_back(Batch());
Batch new_batch;
new_batch.instance_buffer_index = state.current_instance_buffer_index;
state.canvas_instance_batches.push_back(new_batch);
return;
}
@ -1619,7 +1634,7 @@ void RasterizerCanvasGLES3::light_set_use_shadow(RID p_rid, bool p_enable) {
cl->shadow.enabled = p_enable;
}
void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) {
void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) {
GLES3::Config *config = GLES3::Config::get_singleton();
CanvasLight *cl = canvas_light_owner.get_or_null(p_rid);
@ -1655,28 +1670,39 @@ void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, c
return;
}
Projection projection;
{
real_t fov = 90;
real_t nearp = p_near;
real_t farp = p_far;
real_t aspect = 1.0;
real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5));
real_t ymin = -ymax;
real_t xmin = ymin * aspect;
real_t xmax = ymax * aspect;
projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
}
// Precomputed:
// Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
// projection = projection * Projection(Transform3D().looking_at(cam_targets[i], Vector3(0, 0, -1)).affine_inverse());
const Projection projections[4] = {
projection * Projection(Vector4(0, 0, -1, 0), Vector4(1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
projection * Projection(Vector4(-1, 0, 0, 0), Vector4(0, 0, -1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
projection * Projection(Vector4(0, 0, 1, 0), Vector4(-1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
projection * Projection(Vector4(1, 0, 0, 0), Vector4(0, 0, 1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1))
};
for (int i = 0; i < 4; i++) {
glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2);
Projection projection;
{
real_t fov = 90;
real_t nearp = p_near;
real_t farp = p_far;
real_t aspect = 1.0;
real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5));
real_t ymin = -ymax;
real_t xmin = ymin * aspect;
real_t xmax = ymax * aspect;
projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
}
Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse());
shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projection, shadow_render.shader_version, variant);
shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projections[i], shadow_render.shader_version, variant);
static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) };
shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, directions[i].x, directions[i].y, shadow_render.shader_version, variant);
@ -1735,7 +1761,7 @@ void RasterizerCanvasGLES3::light_update_directional_shadow(RID p_rid, int p_sha
Vector2 center = p_clip_rect.get_center();
float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(light_dir)) - light_dir.dot(center));
float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(-light_dir)) - light_dir.dot(center));
Vector2 from_pos = center - light_dir * (to_edge_distance + p_cull_distance);
float distance = to_edge_distance * 2.0 + p_cull_distance;
@ -2170,7 +2196,7 @@ bool RasterizerCanvasGLES3::free(RID p_rid) {
void RasterizerCanvasGLES3::update() {
}
void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backbuffer) {
void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backbuffer, bool p_backbuffer_has_mipmaps) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
@ -2185,6 +2211,9 @@ void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backb
glBindFramebuffer(GL_FRAMEBUFFER, render_target->fbo);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4);
glBindTexture(GL_TEXTURE_2D, render_target->backbuffer);
if (render_target->backbuffer != 0) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, p_backbuffer_has_mipmaps ? render_target->mipmap_count - 1 : 0);
}
}
if (render_target->is_transparent || p_to_backbuffer) {
@ -2345,21 +2374,21 @@ void RasterizerCanvasGLES3::_prepare_canvas_texture(RID p_texture, RS::CanvasIte
GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map);
if (ct->specular_color.a < 0.999) {
state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED;
} else {
state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED;
state.canvas_instance_batches[state.current_batch_index].flags &= ~BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
if (normal_map) {
state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED;
} else {
state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED;
state.canvas_instance_batches[state.current_batch_index].flags &= ~BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED;
}
state.instance_data_array[r_index].specular_shininess = uint32_t(CLAMP(ct->specular_color.a * 255.0, 0, 255)) << 24;
state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.b * 255.0, 0, 255)) << 16;
state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8;
state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255));
state.canvas_instance_batches[state.current_batch_index].specular_shininess = uint32_t(CLAMP(ct->specular_color.a * 255.0, 0, 255)) << 24;
state.canvas_instance_batches[state.current_batch_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.b * 255.0, 0, 255)) << 16;
state.canvas_instance_batches[state.current_batch_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8;
state.canvas_instance_batches[state.current_batch_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255));
r_texpixel_size.x = 1.0 / float(size_cache.x);
r_texpixel_size.y = 1.0 / float(size_cache.y);

View file

@ -54,29 +54,27 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
_FORCE_INLINE_ void _update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4);
enum {
INSTANCE_FLAGS_LIGHT_COUNT_SHIFT = 0, // 4 bits for light count.
FLAGS_INSTANCING_MASK = 0x7F,
FLAGS_INSTANCING_HAS_COLORS = (1 << 7),
FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
INSTANCE_FLAGS_CLIP_RECT_UV = (1 << 4),
INSTANCE_FLAGS_TRANSPOSE_RECT = (1 << 5),
INSTANCE_FLAGS_USE_MSDF = (1 << 6),
INSTANCE_FLAGS_USE_LCD = (1 << 7),
FLAGS_CLIP_RECT_UV = (1 << 9),
FLAGS_TRANSPOSE_RECT = (1 << 10),
INSTANCE_FLAGS_NINEPACH_DRAW_CENTER = (1 << 8),
INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT = 9,
INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT = 11,
FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
INSTANCE_FLAGS_SHADOW_MASKED_SHIFT = 13, // 16 bits.
};
FLAGS_USE_SKELETON = (1 << 15),
FLAGS_NINEPATCH_H_MODE_SHIFT = 16,
FLAGS_NINEPATCH_V_MODE_SHIFT = 18,
FLAGS_LIGHT_COUNT_SHIFT = 20,
enum {
BATCH_FLAGS_INSTANCING_MASK = 0x7F,
BATCH_FLAGS_INSTANCING_HAS_COLORS = (1 << 7),
BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
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),
BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 9),
BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 10),
};
enum {
@ -229,7 +227,7 @@ public:
};
};
uint32_t flags;
uint32_t specular_shininess;
uint32_t instance_uniforms_ofs;
uint32_t lights[4];
};
@ -273,13 +271,16 @@ public:
RID material;
GLES3::CanvasMaterialData *material_data = nullptr;
CanvasShaderGLES3::ShaderVariant shader_variant = CanvasShaderGLES3::MODE_QUAD;
uint64_t vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV;
uint64_t specialization = 0;
const Item::Command *command = nullptr;
Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch.
uint32_t primitive_points = 0;
uint32_t flags = 0;
uint32_t specular_shininess = 0.0;
bool lights_disabled = false;
};
@ -335,7 +336,7 @@ public:
typedef void Texture;
void canvas_begin(RID p_to_render_target, bool p_to_backbuffer);
void canvas_begin(RID p_to_render_target, bool p_to_backbuffer, bool p_backbuffer_has_mipmaps);
//virtual void draw_window_margins(int *black_margin, RID *black_image) override;
void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
@ -345,7 +346,7 @@ public:
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_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) override;
void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override;
void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override;
@ -361,8 +362,8 @@ public:
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 *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_offset);
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr, bool p_backbuffer_has_mipmaps = false);
void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_repeat_offset);
void _render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info = nullptr);
bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization);
void _new_batch(bool &r_batch_broken);
@ -375,10 +376,12 @@ public:
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.");
WARN_PRINT_ONCE("Debug CanvasItem Redraw is not available yet when using the Compatibility renderer.");
}
}
virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) override { return 0; }
static RasterizerCanvasGLES3 *get_singleton();
RasterizerCanvasGLES3();
~RasterizerCanvasGLES3();

View file

@ -35,6 +35,7 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/io/image.h"
#include "core/os/os.h"
#include "storage/texture_storage.h"
@ -62,6 +63,10 @@
#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148
#define _EXT_DEBUG_OUTPUT 0x92E0
#ifndef GL_FRAMEBUFFER_SRGB
#define GL_FRAMEBUFFER_SRGB 0x8DB9
#endif
#ifndef GLAPIENTRY
#if defined(WINDOWS_ENABLED)
#define GLAPIENTRY APIENTRY
@ -82,6 +87,10 @@
#define strcpy strcpy_s
#endif
#ifdef WINDOWS_ENABLED
bool RasterizerGLES3::screen_flipped_y = false;
#endif
bool RasterizerGLES3::gles_over_gl = true;
void RasterizerGLES3::begin_frame(double frame_step) {
@ -209,6 +218,7 @@ void RasterizerGLES3::finalize() {
memdelete(glow);
memdelete(cubemap_filter);
memdelete(copy_effects);
memdelete(feed_effects);
memdelete(light_storage);
memdelete(particles_storage);
memdelete(mesh_storage);
@ -357,10 +367,16 @@ RasterizerGLES3::RasterizerGLES3() {
cubemap_filter = memnew(GLES3::CubemapFilter);
glow = memnew(GLES3::Glow);
post_effects = memnew(GLES3::PostEffects);
feed_effects = memnew(GLES3::FeedEffects);
gi = memnew(GLES3::GI);
fog = memnew(GLES3::Fog);
canvas = memnew(RasterizerCanvasGLES3());
scene = memnew(RasterizerSceneGLES3());
// Disable OpenGL linear to sRGB conversion, because Godot will always do this conversion itself.
if (config->srgb_framebuffer_supported) {
glDisable(GL_FRAMEBUFFER_SRGB);
}
}
RasterizerGLES3::~RasterizerGLES3() {
@ -380,6 +396,12 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
flip_y = false;
}
#ifdef WINDOWS_ENABLED
if (screen_flipped_y) {
flip_y = !flip_y;
}
#endif
GLuint read_fbo = 0;
glGenFramebuffers(1, &read_fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo);
@ -448,9 +470,9 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c
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);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
glClearColor(p_color.r, p_color.g, p_color.b, 1.0);
glClearColor(p_color.r, p_color.g, p_color.b, OS::get_singleton()->is_layered_allowed() ? p_color.a : 1.0);
glClear(GL_COLOR_BUFFER_BIT);
RID texture = texture_storage->texture_allocate();
@ -476,9 +498,14 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c
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;
#ifdef WINDOWS_ENABLED
if (!screen_flipped_y)
#endif
{
// Flip Y.
screenrect.position.y = win_size.y - screenrect.position.y;
screenrect.size.y = -screenrect.size.y;
}
// Normalize texture coordinates to window size.
screenrect.position /= win_size;

View file

@ -35,6 +35,7 @@
#include "effects/copy_effects.h"
#include "effects/cubemap_filter.h"
#include "effects/feed_effects.h"
#include "effects/glow.h"
#include "effects/post_effects.h"
#include "environment/fog.h"
@ -58,6 +59,10 @@ private:
double time_total = 0.0;
bool flip_xy_workaround = false;
#ifdef WINDOWS_ENABLED
static bool screen_flipped_y;
#endif
static bool gles_over_gl;
protected:
@ -74,6 +79,7 @@ protected:
GLES3::CubemapFilter *cubemap_filter = nullptr;
GLES3::Glow *glow = nullptr;
GLES3::PostEffects *post_effects = nullptr;
GLES3::FeedEffects *feed_effects = nullptr;
RasterizerCanvasGLES3 *canvas = nullptr;
RasterizerSceneGLES3 *scene = nullptr;
static RasterizerGLES3 *singleton;
@ -113,13 +119,21 @@ public:
static void make_current(bool p_gles_over_gl) {
gles_over_gl = p_gles_over_gl;
OS::get_singleton()->set_gles_over_gl(gles_over_gl);
_create_func = _create_current;
low_end = true;
}
#ifdef WINDOWS_ENABLED
static void set_screen_flipped_y(bool p_flipped) {
screen_flipped_y = p_flipped;
}
#endif
_ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; }
_ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; }
_ALWAYS_INLINE_ double get_total_time() const { return time_total; }
_ALWAYS_INLINE_ bool can_create_resources_async() const { return false; }
static RasterizerGLES3 *get_singleton() { return singleton; }
RasterizerGLES3();

View file

@ -31,6 +31,7 @@
#include "rasterizer_scene_gles3.h"
#include "drivers/gles3/effects/copy_effects.h"
#include "drivers/gles3/effects/feed_effects.h"
#include "rasterizer_gles3.h"
#include "storage/config.h"
#include "storage/mesh_storage.h"
@ -39,6 +40,8 @@
#include "core/config/project_settings.h"
#include "core/templates/sort_array.h"
#include "servers/camera/camera_feed.h"
#include "servers/camera_server.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/rendering/rendering_server_globals.h"
@ -235,7 +238,7 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry
GLES3::SceneMaterialData *material_shadow = nullptr;
void *surface_shadow = nullptr;
if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_world_coordinates) {
if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_world_coordinates && !p_material->shader_data->wireframe) {
flags |= GeometryInstanceSurface::FLAG_USES_SHARED_SHADOW_MATERIAL;
material_shadow = static_cast<GLES3::SceneMaterialData *>(GLES3::MaterialStorage::get_singleton()->material_get_data(scene_globals.default_material, RS::SHADER_SPATIAL));
@ -777,7 +780,6 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
ERR_FAIL_COND(p_env.is_null());
Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env));
ERR_FAIL_NULL(sky);
GLES3::SkyMaterialData *material_data = nullptr;
RID sky_material;
@ -851,6 +853,15 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::SKY_ENERGY_MULTIPLIER, p_sky_energy_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
Color fog_color = environment_get_fog_light_color(p_env).srgb_to_linear() * environment_get_fog_light_energy(p_env);
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_ENABLED, environment_get_fog_enabled(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_AERIAL_PERSPECTIVE, environment_get_fog_aerial_perspective(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_LIGHT_COLOR, fog_color, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_SUN_SCATTER, environment_get_fog_sun_scatter(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_DENSITY, environment_get_fog_density(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_SKY_AFFECT, environment_get_fog_sky_affect(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::DIRECTIONAL_LIGHT_COUNT, sky_globals.directional_light_count, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
if (p_use_multiview) {
glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
@ -904,7 +915,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
RS::SkyMode sky_mode = sky->mode;
if (sky_mode == RS::SKY_MODE_AUTOMATIC) {
if (shader_data->uses_time || shader_data->uses_position) {
if ((shader_data->uses_time || shader_data->uses_position) && sky->radiance_size == 256) {
update_single_frame = true;
sky_mode = RS::SKY_MODE_REALTIME;
} else if (shader_data->uses_light || shader_data->ubo_size > 0) {
@ -925,7 +936,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
int max_processing_layer = sky->mipmap_count;
// Update radiance cubemap
if (sky->reflection_dirty && (sky->processing_layer > max_processing_layer || update_single_frame)) {
if (sky->reflection_dirty && (sky->processing_layer >= max_processing_layer || update_single_frame)) {
static const Vector3 view_normals[6] = {
Vector3(+1, 0, 0),
Vector3(-1, 0, 0),
@ -966,7 +977,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
glBindFramebuffer(GL_FRAMEBUFFER, sky->radiance_framebuffer);
scene_state.reset_gl_state();
scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
scene_state.set_gl_cull_mode(RS::CULL_MODE_DISABLED);
scene_state.enable_gl_blend(false);
for (int i = 0; i < 6; i++) {
@ -989,7 +1000,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
} else {
if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) {
scene_state.reset_gl_state();
scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
scene_state.set_gl_cull_mode(RS::CULL_MODE_DISABLED);
scene_state.enable_gl_blend(false);
cubemap_filter->filter_radiance(sky->raw_radiance, sky->radiance, sky->radiance_framebuffer, sky->radiance_size, sky->mipmap_count, sky->processing_layer);
@ -1291,15 +1302,12 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
int32_t shadow_id = light_storage->light_instance_get_shadow_id(light_instance);
if (light_storage->light_has_shadow(light) && shadow_id >= 0) {
// Skip static lights when a lightmap is used.
if (!inst->lightmap_instance.is_valid() || light_storage->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
GeometryInstanceGLES3::LightPass pass;
pass.light_id = light_storage->light_instance_get_gl_id(light_instance);
pass.shadow_id = shadow_id;
pass.light_instance_rid = light_instance;
pass.is_omni = true;
inst->light_passes.push_back(pass);
}
GeometryInstanceGLES3::LightPass pass;
pass.light_id = light_storage->light_instance_get_gl_id(light_instance);
pass.shadow_id = shadow_id;
pass.light_instance_rid = light_instance;
pass.is_omni = true;
inst->light_passes.push_back(pass);
} else {
// Lights without shadow can all go in base pass.
inst->omni_light_gl_cache.push_back((uint32_t)light_storage->light_instance_get_gl_id(light_instance));
@ -1317,14 +1325,11 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
int32_t shadow_id = light_storage->light_instance_get_shadow_id(light_instance);
if (light_storage->light_has_shadow(light) && shadow_id >= 0) {
// Skip static lights when a lightmap is used.
if (!inst->lightmap_instance.is_valid() || light_storage->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
GeometryInstanceGLES3::LightPass pass;
pass.light_id = light_storage->light_instance_get_gl_id(light_instance);
pass.shadow_id = shadow_id;
pass.light_instance_rid = light_instance;
inst->light_passes.push_back(pass);
}
GeometryInstanceGLES3::LightPass pass;
pass.light_id = light_storage->light_instance_get_gl_id(light_instance);
pass.shadow_id = shadow_id;
pass.light_instance_rid = light_instance;
inst->light_passes.push_back(pass);
} else {
// Lights without shadow can all go in base pass.
inst->spot_light_gl_cache.push_back((uint32_t)light_storage->light_instance_get_gl_id(light_instance));
@ -1355,38 +1360,25 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
GeometryInstanceSurface *surf = inst->surface_caches;
float lod_distance = 0.0;
if (p_render_data->cam_orthogonal) {
lod_distance = 1.0;
} else {
Vector3 aabb_min = inst->transformed_aabb.position;
Vector3 aabb_max = inst->transformed_aabb.position + inst->transformed_aabb.size;
Vector3 camera_position = p_render_data->main_cam_transform.origin;
Vector3 surface_distance = Vector3(0.0, 0.0, 0.0).max(aabb_min - camera_position).max(camera_position - aabb_max);
lod_distance = surface_distance.length();
}
while (surf) {
// LOD
if (p_render_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) {
float distance = 0.0;
// Check if camera is NOT inside the mesh AABB.
if (!inst->transformed_aabb.has_point(p_render_data->main_cam_transform.origin)) {
// Get the LOD support points on the mesh AABB.
Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z));
Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z));
// Get the distances to those points on the AABB from the camera origin.
float distance_min = (float)p_render_data->main_cam_transform.origin.distance_to(lod_support_min);
float distance_max = (float)p_render_data->main_cam_transform.origin.distance_to(lod_support_max);
if (distance_min * distance_max < 0.0) {
//crossing plane
distance = 0.0;
} else if (distance_min >= 0.0) {
distance = distance_min;
} else if (distance_max <= 0.0) {
distance = -distance_max;
}
}
if (p_render_data->cam_orthogonal) {
distance = 1.0;
}
uint32_t indices = 0;
surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, indices);
surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, lod_distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, indices);
surf->index_count = indices;
if (p_render_data->render_info) {
@ -1441,6 +1433,10 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
if (surf->flags & GeometryInstanceSurface::FLAG_PASS_SHADOW) {
rl->add_element(surf);
}
} else if (p_pass_mode == PASS_MODE_MATERIAL) {
if (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE | GeometryInstanceSurface::FLAG_PASS_ALPHA)) {
rl->add_element(surf);
}
} else {
if (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE)) {
rl->add_element(surf);
@ -2137,7 +2133,7 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas,
light_transform = light_storage->light_instance_get_shadow_transform(p_light, p_pass);
shadow_size = shadow_size / 2;
} else {
ERR_FAIL_MSG("Dual paraboloid shadow mode not supported in GL Compatibility renderer. Please use Cubemap shadow mode instead.");
ERR_FAIL_MSG("Dual paraboloid shadow mode not supported in the Compatibility renderer. Please use CubeMap shadow mode instead.");
}
shadow_bias = light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS);
@ -2218,7 +2214,7 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas,
scene_state.enable_gl_depth_test(false);
scene_state.enable_gl_depth_draw(true);
glDisable(GL_CULL_FACE);
scene_state.cull_mode = GLES3::SceneShaderData::CULL_DISABLED;
scene_state.cull_mode = RS::CULL_MODE_DISABLED;
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
@ -2258,12 +2254,18 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
RenderDataGLES3 render_data;
{
render_data.render_buffers = rb;
render_data.transparent_bg = rt ? rt->is_transparent : false;
if (rt) {
render_data.transparent_bg = rt->is_transparent;
render_data.render_region = rt->render_region;
}
// Our first camera is used by default
render_data.cam_transform = p_camera_data->main_transform;
render_data.inv_cam_transform = render_data.cam_transform.affine_inverse();
render_data.cam_projection = p_camera_data->main_projection;
render_data.cam_orthogonal = p_camera_data->is_orthogonal;
render_data.cam_frustum = p_camera_data->is_frustum;
render_data.camera_visible_layers = p_camera_data->visible_layers;
render_data.main_cam_transform = p_camera_data->main_transform;
@ -2387,7 +2389,10 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
bool draw_sky = false;
bool draw_sky_fog_only = false;
bool keep_color = false;
bool draw_canvas = false;
bool draw_feed = false;
float sky_energy_multiplier = 1.0;
int camera_feed_id = -1;
if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW)) {
clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black
@ -2426,12 +2431,15 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
draw_sky = !render_data.transparent_bg;
} break;
case RS::ENV_BG_CANVAS: {
keep_color = true;
draw_canvas = true;
} break;
case RS::ENV_BG_KEEP: {
keep_color = true;
} break;
case RS::ENV_BG_CAMERA_FEED: {
camera_feed_id = environment_get_camera_feed_id(render_data.environment);
draw_feed = true;
keep_color = true;
} break;
default: {
}
@ -2490,6 +2498,10 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
RENDER_TIMESTAMP("Depth Prepass");
//pre z pass
if (render_data.render_region != Rect2i()) {
glViewport(render_data.render_region.position.x, render_data.render_region.position.y, render_data.render_region.size.width, render_data.render_region.size.height);
}
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
scene_state.enable_gl_blend(false);
@ -2543,10 +2555,14 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glClear(GL_DEPTH_BUFFER_BIT);
}
if (!keep_color) {
// Need to clear framebuffer unless:
// a) We explicitly request not to (i.e. ENV_BG_KEEP).
// b) We are rendering to a non-intermediate framebuffer with ENV_BG_CANVAS (shared between 2D and 3D).
if (!keep_color && (!draw_canvas || fbo != rt->fbo)) {
clear_color.a = render_data.transparent_bg ? 0.0f : 1.0f;
glClearBufferfv(GL_COLOR, 0, clear_color.components);
} else if (fbo != rt->fbo) {
}
if ((keep_color || draw_canvas) && fbo != rt->fbo) {
// Need to copy our current contents to our intermediate/MSAA buffer
GLES3::CopyEffects *copy_effects = GLES3::CopyEffects::get_singleton();
@ -2557,11 +2573,18 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glBindTexture(rt->view_count > 1 ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D, rt->color);
copy_effects->copy_screen(render_data.luminance_multiplier);
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
}
RENDER_TIMESTAMP("Render Opaque Pass");
uint64_t spec_constant_base_flags = 0;
if (render_data.render_region != Rect2i()) {
glViewport(render_data.render_region.position.x, render_data.render_region.position.y, render_data.render_region.size.width, render_data.render_region.size.height);
}
{
// Specialization Constants that apply for entire rendering pass.
if (render_data.directional_light_count == 0) {
@ -2580,6 +2603,29 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
spec_constant_base_flags |= SceneShaderGLES3::APPLY_TONEMAPPING;
}
}
if (draw_feed && camera_feed_id > -1) {
RENDER_TIMESTAMP("Render Camera feed");
scene_state.enable_gl_depth_draw(false);
scene_state.enable_gl_depth_test(false);
scene_state.enable_gl_blend(false);
scene_state.set_gl_cull_mode(RS::CULL_MODE_BACK);
Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
if (feed.is_valid()) {
RID camera_YCBCR = feed->get_texture(CameraServer::FEED_YCBCR_IMAGE);
GLES3::TextureStorage::get_singleton()->texture_bind(camera_YCBCR, 0);
GLES3::FeedEffects *feed_effects = GLES3::FeedEffects::get_singleton();
feed_effects->draw();
}
scene_state.enable_gl_depth_draw(true);
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_blend(true);
}
// Render Opaque Objects.
RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe);
@ -2587,21 +2633,25 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.enable_gl_depth_draw(false);
if (draw_sky) {
if (draw_sky || draw_sky_fog_only) {
RENDER_TIMESTAMP("Render Sky");
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_blend(false);
scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK);
scene_state.set_gl_cull_mode(RS::CULL_MODE_BACK);
Transform3D transform = render_data.cam_transform;
Projection projection = render_data.cam_projection;
if (is_reflection_probe) {
Projection correction;
correction.columns[1][1] = -1.0;
projection = correction * render_data.cam_projection;
} else if (render_data.cam_frustum) {
// Sky is drawn upside down, the frustum offset doesn't know the image is upside down so needs a flip.
projection[2].y = -projection[2].y;
}
_draw_sky(render_data.environment, projection, render_data.cam_transform, sky_energy_multiplier, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post);
_draw_sky(render_data.environment, projection, transform, sky_energy_multiplier, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post);
}
if (rt && (scene_state.used_screen_texture || scene_state.used_depth_texture)) {
@ -2633,14 +2683,14 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glBlitFramebuffer(0, 0, size.x, size.y,
0, 0, size.x, size.y,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6);
glBindTexture(GL_TEXTURE_2D, backbuffer);
}
if (scene_state.used_depth_texture) {
glBlitFramebuffer(0, 0, size.x, size.y,
0, 0, size.x, size.y,
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
glBindTexture(GL_TEXTURE_2D, backbuffer_depth);
}
}
@ -3072,19 +3122,19 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
// Find cull variant.
GLES3::SceneShaderData::Cull cull_mode = shader->cull_mode;
RS::CullMode cull_mode = shader->cull_mode;
if (p_pass_mode == PASS_MODE_MATERIAL || (surf->flags & GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS)) {
cull_mode = GLES3::SceneShaderData::CULL_DISABLED;
cull_mode = RS::CULL_MODE_DISABLED;
} else {
bool mirror = inst->mirror;
if (p_params->reverse_cull) {
mirror = !mirror;
}
if (cull_mode == GLES3::SceneShaderData::CULL_FRONT && mirror) {
cull_mode = GLES3::SceneShaderData::CULL_BACK;
} else if (cull_mode == GLES3::SceneShaderData::CULL_BACK && mirror) {
cull_mode = GLES3::SceneShaderData::CULL_FRONT;
if (cull_mode == RS::CULL_MODE_FRONT && mirror) {
cull_mode = RS::CULL_MODE_BACK;
} else if (cull_mode == RS::CULL_MODE_BACK && mirror) {
cull_mode = RS::CULL_MODE_FRONT;
}
}
@ -3124,7 +3174,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
bool use_wireframe = false;
if (p_params->force_wireframe) {
if (p_params->force_wireframe || shader->wireframe) {
GLuint wireframe_index_array_gl = mesh_storage->mesh_surface_get_index_buffer_wireframe(mesh_surface);
if (wireframe_index_array_gl) {
index_array_gl = wireframe_index_array_gl;
@ -3202,6 +3252,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
if (lm->uses_spherical_harmonics) {
spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
}
if (lightmap_bicubic_upscale) {
spec_constants |= SceneShaderGLES3::LIGHTMAP_BICUBIC_FILTER;
}
} else if (inst->lightmap_sh) {
spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
} else {
@ -3214,8 +3268,28 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;
bool disable_lightmaps = true;
// Additive directional passes may use shadowmasks, so enable lightmaps for them.
if (pass >= int32_t(inst->light_passes.size()) && inst->lightmap_instance.is_valid()) {
GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
if (lm->shadowmask_mode != RS::SHADOWMASK_MODE_NONE) {
spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
disable_lightmaps = false;
if (lightmap_bicubic_upscale) {
spec_constants |= SceneShaderGLES3::LIGHTMAP_BICUBIC_FILTER;
}
}
}
if (disable_lightmaps) {
spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
}
}
if (uses_additive_lighting) {
@ -3277,10 +3351,6 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant, spec_constants);
prev_shader = shader;
prev_variant = instance_variant;
prev_spec_constants = spec_constants;
}
// Pass in lighting uniforms.
@ -3314,11 +3384,38 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
GLuint tex = GLES3::LightStorage::get_singleton()->directional_shadow_get_texture();
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 3);
glBindTexture(GL_TEXTURE_2D, tex);
if (inst->lightmap_instance.is_valid()) {
// Use shadowmasks for directional light passes.
GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SLICE, inst->lightmap_slice_index, shader->version, instance_variant, spec_constants);
Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants);
if (lightmap_bicubic_upscale) {
Vector2 light_texture_size(lm->light_texture_size.x, lm->light_texture_size.y);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_TEXTURE_SIZE, light_texture_size, shader->version, instance_variant, spec_constants);
}
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SHADOWMASK_MODE, (uint32_t)lm->shadowmask_mode, shader->version, instance_variant, spec_constants);
if (lm->shadow_texture.is_valid()) {
tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lm->shadow_texture);
} else {
tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(GLES3::TextureStorage::get_singleton()->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE));
}
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5);
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
}
}
}
// Pass light count and array of light indices for base pass.
if ((prev_inst != inst || prev_shader != shader || prev_variant != instance_variant) && pass == 0) {
if ((prev_inst != inst || prev_shader != shader || prev_variant != instance_variant || prev_spec_constants != spec_constants) && pass == 0) {
// Rebind the light indices.
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_gl_cache.size(), shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_gl_cache.size(), shader->version, instance_variant, spec_constants);
@ -3344,6 +3441,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants);
if (lightmap_bicubic_upscale) {
Vector2 light_texture_size(lm->light_texture_size.x, lm->light_texture_size.y);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_TEXTURE_SIZE, light_texture_size, shader->version, instance_variant, spec_constants);
}
float exposure_normalization = 1.0;
if (p_render_data->camera_attributes.is_valid()) {
float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes);
@ -3367,14 +3469,18 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
};
glUniformMatrix3fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_NORMAL_XFORM, shader->version, instance_variant, spec_constants), 1, GL_FALSE, matrix);
}
} else if (inst->lightmap_sh) {
glUniform4fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURES, shader->version, instance_variant, spec_constants), 9, reinterpret_cast<const GLfloat *>(inst->lightmap_sh->sh));
}
prev_inst = inst;
}
}
prev_shader = shader;
prev_variant = instance_variant;
prev_spec_constants = spec_constants;
// Pass in reflection probe data
if constexpr (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) {
if (pass == 0 && inst->reflection_probe_rid_cache.size() > 0) {
@ -3394,8 +3500,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_AMBIENT_MODE, int(probe->ambient_mode), shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_AMBIENT_COLOR, probe->ambient_color * probe->ambient_color_energy, shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_LOCAL_MATRIX, inst->reflection_probes_local_transform_cache[0], shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_BLEND_DISTANCE, probe->blend_distance, shader->version, instance_variant, spec_constants);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 8);
glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[0]));
}
@ -3412,8 +3519,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_AMBIENT_MODE, int(probe->ambient_mode), shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_AMBIENT_COLOR, probe->ambient_color * probe->ambient_color_energy, shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_LOCAL_MATRIX, inst->reflection_probes_local_transform_cache[1], shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_BLEND_DISTANCE, probe->blend_distance, shader->version, instance_variant, spec_constants);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 8);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 9);
glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[1]));
spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
@ -3436,6 +3544,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::MODEL_FLAGS, inst->flags_cache, shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::INSTANCE_OFFSET, uint32_t(inst->shader_uniforms_offset), shader->version, instance_variant, spec_constants);
if (p_pass_mode == PASS_MODE_MATERIAL) {
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::UV_OFFSET, p_params->uv_offset, shader->version, instance_variant, spec_constants);
@ -3746,7 +3855,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
glActiveTexture(GL_TEXTURE0);
scene_state.enable_gl_depth_draw(true);
glDepthFunc(GL_ALWAYS);
scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
scene_state.set_gl_cull_mode(RS::CULL_MODE_DISABLED);
// Loop through quadrants and copy shadows over.
for (int quadrant = 0; quadrant < 4; quadrant++) {
@ -3882,7 +3991,7 @@ TypedArray<Image> RasterizerSceneGLES3::bake_render_uv2(RID p_base, const TypedA
// Consider rendering to RGBA8 encoded as RGBE, then manually convert to RGBAH on CPU.
glBindTexture(GL_TEXTURE_2D, emission_tex);
if (config->float_texture_supported) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, p_image_size.width, p_image_size.height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, p_image_size.width, p_image_size.height, 0, GL_RGBA, GL_FLOAT, nullptr);
GLES3::Utilities::get_singleton()->texture_allocated_data(emission_tex, p_image_size.width * p_image_size.height * 16, "Lightmap emission texture");
} else {
// Fallback to RGBA8 on devices that don't support rendering to floating point textures. This will look bad, but we have no choice.
@ -3985,9 +4094,9 @@ TypedArray<Image> RasterizerSceneGLES3::bake_render_uv2(RID p_base, const TypedA
{
tex->tex_id = emission_tex;
if (config->float_texture_supported) {
tex->format = Image::FORMAT_RGBAF;
tex->format = Image::FORMAT_RGBAH;
tex->real_format = Image::FORMAT_RGBAH;
tex->gl_type_cache = GL_FLOAT;
tex->gl_type_cache = GL_HALF_FLOAT;
}
Ref<Image> img = GLES3::TextureStorage::get_singleton()->texture_2d_get(tex_rid);
GLES3::Utilities::get_singleton()->texture_free_data(emission_tex);
@ -4039,6 +4148,10 @@ void RasterizerSceneGLES3::decals_set_filter(RS::DecalFilter p_filter) {
void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter p_filter) {
}
void RasterizerSceneGLES3::lightmaps_set_bicubic_filter(bool p_enable) {
lightmap_bicubic_upscale = p_enable;
}
RasterizerSceneGLES3::RasterizerSceneGLES3() {
singleton = this;
@ -4052,6 +4165,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() {
positional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality"));
directional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality"));
lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter"));
{
// Setup Lights
@ -4113,6 +4227,9 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() {
global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "u\n";
global_defines += "\n#define MAX_ROUGHNESS_LOD " + itos(sky_globals.roughness_layers - 1) + ".0\n";
if (config->force_vertex_shading) {
global_defines += "\n#define USE_VERTEX_LIGHTING\n";
}
material_storage->shaders.scene_shader.initialize(global_defines);
scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create();
material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR);

View file

@ -98,11 +98,13 @@ enum SkyUniformLocation {
struct RenderDataGLES3 {
Ref<RenderSceneBuffersGLES3> render_buffers;
bool transparent_bg = false;
Rect2i render_region;
Transform3D cam_transform;
Transform3D inv_cam_transform;
Projection cam_projection;
bool cam_orthogonal = false;
bool cam_frustum = false;
uint32_t camera_visible_layers = 0xFFFFFFFF;
// For billboards to cast correct shadows.
@ -248,6 +250,10 @@ private:
};
union {
struct {
uint64_t sort_key1;
uint64_t sort_key2;
};
struct {
uint64_t lod_index : 8;
uint64_t surface_index : 8;
@ -263,10 +269,6 @@ private:
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;
@ -460,7 +462,7 @@ private:
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;
RS::CullMode cull_mode = RS::CULL_MODE_BACK;
bool current_blend_enabled = false;
bool current_depth_draw_enabled = false;
@ -476,7 +478,7 @@ private:
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
cull_mode = GLES3::SceneShaderData::CULL_BACK;
cull_mode = RS::CULL_MODE_BACK;
glDepthMask(GL_FALSE);
current_depth_draw_enabled = false;
@ -484,16 +486,16 @@ private:
current_depth_test_enabled = false;
}
void set_gl_cull_mode(GLES3::SceneShaderData::Cull p_mode) {
void set_gl_cull_mode(RS::CullMode p_mode) {
if (cull_mode != p_mode) {
if (p_mode == GLES3::SceneShaderData::CULL_DISABLED) {
if (p_mode == RS::CULL_MODE_DISABLED) {
glDisable(GL_CULL_FACE);
} else {
if (cull_mode == GLES3::SceneShaderData::CULL_DISABLED) {
if (cull_mode == RS::CULL_MODE_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);
glCullFace(p_mode == RS::CULL_MODE_FRONT ? GL_FRONT : GL_BACK);
}
cull_mode = p_mode;
}
@ -680,6 +682,8 @@ protected:
bool glow_bicubic_upscale = false;
RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW;
bool lightmap_bicubic_upscale = false;
/* Sky */
struct SkyGlobals {
@ -765,6 +769,11 @@ public:
uint32_t geometry_instance_get_pair_mask() override;
/* PIPELINES */
virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) override {}
virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) override { return 0; }
/* SDFGI UPDATE */
void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {}
@ -863,6 +872,7 @@ public:
void decals_set_filter(RS::DecalFilter p_filter) override;
void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override;
virtual void lightmaps_set_bicubic_filter(bool p_enable) override;
RasterizerSceneGLES3();
~RasterizerSceneGLES3();

View file

@ -32,7 +32,6 @@
#ifdef GLES3_ENABLED
#include "core/io/compression.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
@ -168,6 +167,10 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant
builder.append("#version 300 es\n");
}
if (GLES3::Config::get_singleton()->polyfill_half2float) {
builder.append("#define USE_HALF2FLOAT\n");
}
for (int i = 0; i < specialization_count; i++) {
if (p_specialization & (uint64_t(1) << uint64_t(i))) {
builder.append("#define " + String(specializations[i].name) + "\n");
@ -189,6 +192,14 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant
}
builder.append("\n"); //make sure defines begin at newline
// Optional support for external textures.
if (GLES3::Config::get_singleton()->external_texture_supported) {
builder.append("#extension GL_OES_EGL_image_external : enable\n");
builder.append("#extension GL_OES_EGL_image_external_essl3 : enable\n");
} else {
builder.append("#define samplerExternalOES sampler2D\n");
}
// Insert multiview extension loading, because it needs to appear before
// any non-preprocessor code (like the "precision highp..." lines below).
builder.append("#ifdef USE_MULTIVIEW\n");
@ -698,7 +709,8 @@ void ShaderGLES3::_clear_version(Version *p_version) {
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)) {
bool use_cache = shader_cache_dir_valid && !(feedback_count > 0 && GLES3::Config::get_singleton()->disable_transform_feedback_shader_cache);
if (use_cache && _load_from_cache(p_version)) {
return;
}
p_version->variants.reserve(variant_count);
@ -709,7 +721,7 @@ void ShaderGLES3::_initialize_version(Version *p_version) {
_compile_specialization(spec, i, p_version, specialization_default_mask);
p_version->variants[i].insert(specialization_default_mask, spec);
}
if (shader_cache_dir_valid) {
if (use_cache) {
_save_to_cache(p_version);
}
}

View file

@ -36,17 +36,13 @@
#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 {

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
@ -16,6 +17,7 @@ if "GLES3_GLSL" in env["BUILDERS"]:
# as we have a few, not yet, converted files we name the ones we want to include:
env.GLES3_GLSL("canvas.glsl")
env.GLES3_GLSL("feed.glsl")
env.GLES3_GLSL("scene.glsl")
env.GLES3_GLSL("sky.glsl")
env.GLES3_GLSL("canvas_occlusion.glsl")

View file

@ -1,17 +1,16 @@
/* clang-format off */
#[modes]
mode_quad =
mode_ninepatch = #define USE_NINEPATCH
mode_primitive = #define USE_PRIMITIVE
mode_attributes = #define USE_ATTRIBUTES
mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING
mode_default =
#[specializations]
DISABLE_LIGHTING = true
USE_RGBA_SHADOWS = false
SINGLE_INSTANCE = false
USE_NINEPATCH = false
USE_PRIMITIVE = false
USE_ATTRIBUTES = false
USE_INSTANCING = false
#[vertex]
@ -84,7 +83,7 @@ layout(location = 15) in highp uvec4 attrib_H;
#endif
#define read_draw_data_flags attrib_G.z
#define read_draw_data_specular_shininess attrib_G.w
#define read_draw_data_instance_offset attrib_G.w
#define read_draw_data_lights attrib_H
// Varyings so the per-instance info can be used in the fragment shader
@ -111,6 +110,9 @@ layout(std140) uniform MaterialUniforms{ //ubo:4
};
#endif
uniform mediump uint batch_flags;
/* clang-format on */
#include "canvas_uniforms_inc.glsl"
@ -140,7 +142,7 @@ void main() {
#endif // !USE_ATTRIBUTES
#endif // USE_PRIMITIVE
varying_F = uvec2(read_draw_data_flags, read_draw_data_specular_shininess);
varying_F = uvec2(read_draw_data_flags, read_draw_data_instance_offset);
varying_G = read_draw_data_lights;
vec4 instance_custom = vec4(0.0);
@ -180,13 +182,13 @@ void main() {
vec2 uv = uv_attrib;
#ifdef USE_INSTANCING
if (bool(read_draw_data_flags & FLAGS_INSTANCING_HAS_COLORS)) {
if (bool(batch_flags & BATCH_FLAGS_INSTANCING_HAS_COLORS)) {
vec4 instance_color;
instance_color.xy = unpackHalf2x16(uint(instance_color_custom_data.x));
instance_color.zw = unpackHalf2x16(uint(instance_color_custom_data.y));
color *= instance_color;
}
if (bool(read_draw_data_flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
if (bool(batch_flags & BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
instance_custom.xy = unpackHalf2x16(instance_color_custom_data.z);
instance_custom.zw = unpackHalf2x16(instance_color_custom_data.w);
}
@ -206,20 +208,21 @@ void main() {
// 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)
if (vertex_id == 0) {
vertex_base = vec2(0.0, 0.0);
else if (vertex_id == 1)
} else if (vertex_id == 1) {
vertex_base = vec2(0.0, 1.0);
else if (vertex_id == 2)
} else if (vertex_id == 2) {
vertex_base = vec2(1.0, 1.0);
else if (vertex_id == 3)
} else if (vertex_id == 3) {
vertex_base = vec2(1.0, 0.0);
else if (vertex_id == 4)
} else if (vertex_id == 4) {
vertex_base = vec2(0.0, 0.0);
else if (vertex_id == 5)
} else if (vertex_id == 5) {
vertex_base = vec2(1.0, 1.0);
}
vec2 uv = read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) * ((read_draw_data_flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy);
vec2 uv = read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) * ((read_draw_data_flags & INSTANCE_FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy);
vec4 color = read_draw_data_modulation;
vec2 vertex = read_draw_data_dst_rect.xy + abs(read_draw_data_dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(read_draw_data_src_rect.zw, vec2(0.0, 0.0)));
@ -262,6 +265,8 @@ void main() {
color_interp = color;
vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
if (use_pixel_snap) {
vertex = floor(vertex + 0.5);
// precision issue on some hardware creates artifacts within texture
@ -269,8 +274,6 @@ void main() {
uv += 1e-5;
}
vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
vertex_interp = vertex;
uv_interp = uv;
@ -323,7 +326,7 @@ flat in vec4 varying_E;
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_instance_offset varying_F.y
#define read_draw_data_lights varying_G
#ifndef DISABLE_LIGHTING
@ -337,6 +340,9 @@ uniform sampler2D specular_texture; //texunit:-7
uniform sampler2D color_texture; //texunit:0
uniform mediump uint batch_flags;
uniform highp uint specular_shininess_in;
layout(location = 0) out vec4 frag_color;
/* clang-format off */
@ -520,7 +526,7 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo
} else if (pixel >= draw_size - margin_end) {
return (tex_size - (draw_size - pixel)) * tex_pixel_size;
} else {
if (!bool(read_draw_data_flags & FLAGS_NINEPACH_DRAW_CENTER)) {
if (!bool(read_draw_data_flags & INSTANCE_FLAGS_NINEPATCH_DRAW_CENTER)) {
draw_center--;
}
@ -568,8 +574,8 @@ void main() {
int draw_center = 2;
uv = vec2(
map_ninepatch_axis(pixel_size_interp.x, abs(read_draw_data_dst_rect_z), read_draw_data_color_texture_pixel_size.x, read_draw_data_ninepatch_margins.x, read_draw_data_ninepatch_margins.z, int(read_draw_data_flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
map_ninepatch_axis(pixel_size_interp.y, abs(read_draw_data_dst_rect_w), read_draw_data_color_texture_pixel_size.y, read_draw_data_ninepatch_margins.y, read_draw_data_ninepatch_margins.w, int(read_draw_data_flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
map_ninepatch_axis(pixel_size_interp.x, abs(read_draw_data_dst_rect_z), read_draw_data_color_texture_pixel_size.x, read_draw_data_ninepatch_margins.x, read_draw_data_ninepatch_margins.z, int(read_draw_data_flags >> INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
map_ninepatch_axis(pixel_size_interp.y, abs(read_draw_data_dst_rect_w), read_draw_data_color_texture_pixel_size.y, read_draw_data_ninepatch_margins.y, read_draw_data_ninepatch_margins.w, int(read_draw_data_flags >> INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
if (draw_center == 0) {
color.a = 0.0;
@ -578,14 +584,15 @@ void main() {
uv = uv * read_draw_data_src_rect.zw + read_draw_data_src_rect.xy; //apply region if needed
#endif
if (bool(read_draw_data_flags & FLAGS_CLIP_RECT_UV)) {
uv = clamp(uv, read_draw_data_src_rect.xy, read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw));
if (bool(read_draw_data_flags & INSTANCE_FLAGS_CLIP_RECT_UV)) {
vec2 half_texpixel = read_draw_data_color_texture_pixel_size * 0.5;
uv = clamp(uv, read_draw_data_src_rect.xy + half_texpixel, read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) - half_texpixel);
}
#endif
#ifndef USE_PRIMITIVE
if (bool(read_draw_data_flags & FLAGS_USE_MSDF)) {
if (bool(read_draw_data_flags & INSTANCE_FLAGS_USE_MSDF)) {
float px_range = read_draw_data_ninepatch_margins.x;
float outline_thickness = read_draw_data_ninepatch_margins.y;
@ -603,7 +610,7 @@ void main() {
float a = clamp(d * px_size + 0.5, 0.0, 1.0);
color.a = a * color.a;
}
} else if (bool(read_draw_data_flags & FLAGS_USE_LCD)) {
} else if (bool(read_draw_data_flags & INSTANCE_FLAGS_USE_LCD)) {
vec4 lcd_sample = texture(color_texture, uv);
if (lcd_sample.a == 1.0) {
color.rgb = lcd_sample.rgb * color.a;
@ -617,7 +624,7 @@ void main() {
color *= texture(color_texture, uv);
}
uint light_count = (read_draw_data_flags >> uint(FLAGS_LIGHT_COUNT_SHIFT)) & uint(0xF); //max 16 lights
uint light_count = read_draw_data_flags & uint(0xF); // Max 16 lights.
bool using_light = light_count > 0u || directional_light_count > 0u;
vec3 normal;
@ -628,17 +635,16 @@ void main() {
bool normal_used = false;
#endif
if (normal_used || (using_light && bool(read_draw_data_flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
if (normal_used || (using_light && bool(batch_flags & BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED))) {
normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
if (bool(read_draw_data_flags & FLAGS_TRANSPOSE_RECT)) {
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
if (bool(read_draw_data_flags & INSTANCE_FLAGS_TRANSPOSE_RECT)) {
normal.xy = normal.yx;
}
if (bool(read_draw_data_flags & FLAGS_FLIP_H)) {
normal.x = -normal.x;
}
if (bool(read_draw_data_flags & FLAGS_FLIP_V)) {
normal.y = -normal.y;
}
normal.xy *= sign(read_draw_data_src_rect.zw);
#endif
normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, normal.xy)));
normal_used = true;
} else {
@ -654,9 +660,9 @@ void main() {
bool specular_shininess_used = false;
#endif
if (specular_shininess_used || (using_light && normal_used && bool(read_draw_data_flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
if (specular_shininess_used || (using_light && normal_used && bool(batch_flags & BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(specular_texture, uv);
specular_shininess *= godot_unpackUnorm4x8(read_draw_data_specular_shininess);
specular_shininess *= godot_unpackUnorm4x8(specular_shininess_in);
specular_shininess_used = true;
} else {
specular_shininess = vec4(1.0);
@ -802,7 +808,7 @@ void main() {
}
#endif
if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW) && bool(read_draw_data_flags & uint(INSTANCE_FLAGS_SHADOW_MASKED << i))) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 pos_norm = normalize(shadow_pos);

View file

@ -1,33 +1,32 @@
#define MAX_LIGHTS_PER_ITEM uint(16)
#define M_PI 3.14159265359
#define SDF_MAX_LENGTH 16384.0
//1 means enabled, 2+ means trails in use
#define FLAGS_INSTANCING_MASK uint(0x7F)
#define FLAGS_INSTANCING_HAS_COLORS uint(1 << 7)
#define FLAGS_INSTANCING_HAS_CUSTOM_DATA uint(1 << 8)
#define INSTANCE_FLAGS_LIGHT_COUNT_SHIFT 0 // 4 bits.
#define FLAGS_CLIP_RECT_UV uint(1 << 9)
#define FLAGS_TRANSPOSE_RECT uint(1 << 10)
// (1 << 11) is for FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR in RD backends, unused here.
#define FLAGS_NINEPACH_DRAW_CENTER uint(1 << 12)
#define INSTANCE_FLAGS_CLIP_RECT_UV uint(1 << 4)
#define INSTANCE_FLAGS_TRANSPOSE_RECT uint(1 << 5)
#define INSTANCE_FLAGS_USE_MSDF uint(1 << 6)
#define INSTANCE_FLAGS_USE_LCD uint(1 << 7)
#define FLAGS_NINEPATCH_H_MODE_SHIFT 16
#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
#define INSTANCE_FLAGS_NINEPATCH_DRAW_CENTER uint(1 << 8)
#define INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT 9
#define INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT 11
#define FLAGS_LIGHT_COUNT_SHIFT 20
#define INSTANCE_FLAGS_SHADOW_MASKED_SHIFT 13u // 16 bits.
#define INSTANCE_FLAGS_SHADOW_MASKED uint(1 << INSTANCE_FLAGS_SHADOW_MASKED_SHIFT)
#define FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 26)
#define FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 27)
// 1 means enabled, 2+ means trails in use
#define BATCH_FLAGS_INSTANCING_MASK uint(0x7F)
#define BATCH_FLAGS_INSTANCING_HAS_COLORS_SHIFT 7
#define BATCH_FLAGS_INSTANCING_HAS_COLORS uint(1 << BATCH_FLAGS_INSTANCING_HAS_COLORS_SHIFT)
#define BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT 8
#define BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA uint(1 << BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT)
#define FLAGS_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)
#define BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 9)
#define BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 10)
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

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

View file

@ -342,7 +342,7 @@ void main() {
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;
attractor_force -= mass * amount * dir * attractors[i].strength;
}
float particle_size = particle_size;
@ -453,14 +453,14 @@ void main() {
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;
float y = texture(height_field_texture, uvw_pos.xz).r;
if (y + EPSILON > uvw_pos.y) {
//inside heightfield
vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz;
vec3 pos2 = (vec3(uvw_pos.x + DELTA, 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;
vec3 pos2 = (vec3(uvw_pos.x + DELTA, texture(height_field_texture, uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz;
vec3 pos3 = (vec3(uvw_pos.x, texture(height_field_texture, uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * colliders[i].extents.xyz;
normal = normalize(cross(pos1 - pos2, pos1 - pos3));
float local_y = (vec3(local_pos / colliders[i].extents.xyz) * 0.5 + 0.5).y;

File diff suppressed because it is too large Load diff

View file

@ -59,7 +59,7 @@ 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
uniform highp sampler2D skeleton_texture; // texunit:0
#endif
/* clang-format on */

View file

@ -2,17 +2,15 @@
#[modes]
mode_background =
mode_half_res = #define USE_HALF_RES_PASS
mode_quarter_res = #define USE_QUARTER_RES_PASS
mode_cubemap = #define USE_CUBEMAP_PASS
mode_cubemap_half_res = #define USE_CUBEMAP_PASS \n#define USE_HALF_RES_PASS
mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PASS
#[specializations]
USE_MULTIVIEW = false
USE_INVERTED_Y = true
APPLY_TONEMAPPING = true
USE_QUARTER_RES_PASS = false
USE_HALF_RES_PASS = false
#[vertex]
@ -108,11 +106,11 @@ uniform float sky_energy_multiplier;
uniform float luminance_multiplier;
uniform float fog_aerial_perspective;
uniform vec3 fog_light_color;
uniform vec4 fog_light_color;
uniform float fog_sun_scatter;
uniform bool fog_enabled;
uniform float fog_density;
uniform float z_far;
uniform float fog_sky_affect;
uniform uint directional_light_count;
#ifdef USE_MULTIVIEW
@ -135,6 +133,24 @@ vec3 interleaved_gradient_noise(vec2 pos) {
}
#endif
#if !defined(DISABLE_FOG)
vec4 fog_process(vec3 view, vec3 sky_color) {
vec3 fog_color = mix(fog_light_color.rgb, sky_color, fog_aerial_perspective);
if (fog_sun_scatter > 0.001) {
vec4 sun_scatter = vec4(0.0);
float sun_total = 0.0;
for (uint i = 0u; i < directional_light_count; i++) {
vec3 light_color = directional_lights.data[i].color_size.xyz * directional_lights.data[i].direction_energy.w;
float light_amount = pow(max(dot(view, directional_lights.data[i].direction_energy.xyz), 0.0), 8.0);
fog_color += light_color * light_amount * fog_sun_scatter;
}
}
return vec4(fog_color, 1.0);
}
#endif // !DISABLE_FOG
void main() {
vec3 cube_normal;
#ifdef USE_MULTIVIEW
@ -194,15 +210,28 @@ void main() {
#endif
{
#CODE : SKY
}
color *= sky_energy_multiplier;
// Convert to Linear for tonemapping so color matches scene shader better
color = srgb_to_linear(color);
#if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS)
// Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky.
if (fog_enabled) {
vec4 fog = fog_process(cube_normal, color.rgb);
color.rgb = mix(color.rgb, fog.rgb, fog.a * fog_sky_affect);
}
if (custom_fog.a > 0.0) {
color.rgb = mix(color.rgb, custom_fog.rgb, custom_fog.a);
}
#endif // DISABLE_FOG
color *= exposure;
#ifdef APPLY_TONEMAPPING
color = apply_tonemapping(color, white);

View file

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

View file

@ -27,6 +27,14 @@ vec3 srgb_to_linear(vec3 color) {
#ifdef APPLY_TONEMAPPING
// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
vec3 tonemap_reinhard(vec3 color, float p_white) {
float white_squared = p_white * p_white;
vec3 white_squared_color = white_squared * color;
// Equivalent to color * (1 + color / white_squared) / (1 + color)
return (white_squared_color + color * color) / (white_squared_color + white_squared);
}
vec3 tonemap_filmic(vec3 color, float p_white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
// also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
@ -76,16 +84,86 @@ vec3 tonemap_aces(vec3 color, float p_white) {
return color_tonemapped / p_white_tonemapped;
}
vec3 tonemap_reinhard(vec3 color, float p_white) {
return (p_white * color + color) / (color * p_white + p_white);
// Polynomial approximation of EaryChow's AgX sigmoid curve.
// x must be within the range [0.0, 1.0]
vec3 agx_contrast_approx(vec3 x) {
// Generated with Excel trendline
// Input data: Generated using python sigmoid with EaryChow's configuration and 57 steps
// Additional padding values were added to give correct intersections at 0.0 and 1.0
// 6th order, intercept of 0.0 to remove an operation and ensure intersection at 0.0
vec3 x2 = x * x;
vec3 x4 = x2 * x2;
return 0.021 * x + 4.0111 * x2 - 25.682 * x2 * x + 70.359 * x4 - 74.778 * x4 * x + 27.069 * x4 * x2;
}
// This is an approximation and simplification of EaryChow's AgX implementation that is used by Blender.
// This code is based off of the script that generates the AgX_Base_sRGB.cube LUT that Blender uses.
// Source: https://github.com/EaryChow/AgX_LUT_Gen/blob/main/AgXBasesRGB.py
vec3 tonemap_agx(vec3 color) {
// Combined linear sRGB to linear Rec 2020 and Blender AgX inset matrices:
const mat3 srgb_to_rec2020_agx_inset_matrix = mat3(
0.54490813676363087053, 0.14044005884001287035, 0.088827411851915368603,
0.37377945959812267119, 0.75410959864013760045, 0.17887712465043811023,
0.081384976686407536266, 0.10543358536857773485, 0.73224999956948382528);
// Combined inverse AgX outset matrix and linear Rec 2020 to linear sRGB matrices.
const mat3 agx_outset_rec2020_to_srgb_matrix = mat3(
1.9645509602733325934, -0.29932243390911083839, -0.16436833806080403409,
-0.85585845117807513559, 1.3264510741502356555, -0.23822464068860595117,
-0.10886710826831608324, -0.027084020983874825605, 1.402665347143271889);
// LOG2_MIN = -10.0
// LOG2_MAX = +6.5
// MIDDLE_GRAY = 0.18
const float min_ev = -12.4739311883324; // log2(pow(2, LOG2_MIN) * MIDDLE_GRAY)
const float max_ev = 4.02606881166759; // log2(pow(2, LOG2_MAX) * MIDDLE_GRAY)
// Large negative values in one channel and large positive values in other
// channels can result in a colour that appears darker and more saturated than
// desired after passing it through the inset matrix. For this reason, it is
// best to prevent negative input values.
// This is done before the Rec. 2020 transform to allow the Rec. 2020
// transform to be combined with the AgX inset matrix. This results in a loss
// of color information that could be correctly interpreted within the
// Rec. 2020 color space as positive RGB values, but it is less common for Godot
// to provide this function with negative sRGB values and therefore not worth
// the performance cost of an additional matrix multiplication.
// A value of 2e-10 intentionally introduces insignificant error to prevent
// log2(0.0) after the inset matrix is applied; color will be >= 1e-10 after
// the matrix transform.
color = max(color, 2e-10);
// Do AGX in rec2020 to match Blender and then apply inset matrix.
color = srgb_to_rec2020_agx_inset_matrix * color;
// Log2 space encoding.
// Must be clamped because agx_contrast_approx may not work
// well with values outside of the range [0.0, 1.0]
color = clamp(log2(color), min_ev, max_ev);
color = (color - min_ev) / (max_ev - min_ev);
// Apply sigmoid function approximation.
color = agx_contrast_approx(color);
// Convert back to linear before applying outset matrix.
color = pow(color, vec3(2.4));
// Apply outset to make the result more chroma-laden and then go back to linear sRGB.
color = agx_outset_rec2020_to_srgb_matrix * color;
// Blender's lusRGB.compensate_low_side is too complex for this shader, so
// simply return the color, even if it has negative components. These negative
// components may be useful for subsequent color adjustments.
return color;
}
#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
#define TONEMAPPER_AGX 4
vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always outputs clamped [0;1] color
vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR
// Ensure color values passed to tonemappers are positive.
// They can be negative in the case of negative lights, which leads to undesired behavior.
if (tonemapper == TONEMAPPER_LINEAR) {
@ -94,8 +172,10 @@ vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always
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
} else if (tonemapper == TONEMAPPER_ACES) {
return tonemap_aces(max(vec3(0.0f), color), p_white);
} else { // TONEMAPPER_AGX
return tonemap_agx(color);
}
}

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -33,7 +33,6 @@
#include "config.h"
#include "../rasterizer_gles3.h"
#include "texture_storage.h"
#ifdef WEB_ENABLED
#include <emscripten/html5_webgl.h>
@ -80,16 +79,19 @@ Config::Config() {
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_hdr_supported = extensions.has("GL_KHR_texture_compression_astc_hdr");
astc_layered_supported = extensions.has("GL_KHR_texture_compression_astc_sliced_3d");
if (RasterizerGLES3::is_gles_over_gl()) {
float_texture_supported = true;
float_texture_linear_supported = true;
etc2_supported = false;
s3tc_supported = true;
rgtc_supported = true; //RGTC - core since OpenGL version 3.0
srgb_framebuffer_supported = true;
} else {
float_texture_supported = extensions.has("GL_EXT_color_buffer_float");
float_texture_linear_supported = extensions.has("GL_OES_texture_float_linear");
etc2_supported = true;
#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
// Some Android devices report support for S3TC but we don't expect that and don't export the textures.
@ -100,6 +102,7 @@ Config::Config() {
s3tc_supported = extensions.has("GL_EXT_texture_compression_dxt1") || extensions.has("GL_EXT_texture_compression_s3tc") || extensions.has("WEBGL_compressed_texture_s3tc");
#endif
rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc");
srgb_framebuffer_supported = extensions.has("GL_EXT_sRGB_write_control");
}
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units);
@ -107,6 +110,11 @@ Config::Config() {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
GLint max_vertex_output;
glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &max_vertex_output);
GLint max_fragment_input;
glGetIntegerv(GL_MAX_FRAGMENT_INPUT_COMPONENTS, &max_fragment_input);
max_shader_varyings = (uint32_t)MIN(max_vertex_output, max_fragment_input) / 4;
// sanity clamp buffer size to 16K..1MB
max_uniform_buffer_size = CLAMP(max_uniform_buffer_size, 16384, 1048576);
@ -121,7 +129,7 @@ Config::Config() {
#ifdef WEB_ENABLED
msaa_supported = (msaa_max_samples > 0);
#else
msaa_supported = extensions.has("GL_EXT_framebuffer_multisample");
msaa_supported = true;
#endif
#ifndef IOS_ENABLED
#ifdef WEB_ENABLED
@ -138,6 +146,7 @@ Config::Config() {
// These are GLES only
rt_msaa_supported = extensions.has("GL_EXT_multisampled_render_to_texture");
rt_msaa_multiview_supported = extensions.has("GL_OVR_multiview_multisampled_render_to_texture");
external_texture_supported = extensions.has("GL_OES_EGL_image_external_essl3");
if (multiview_supported) {
eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR");
@ -166,9 +175,16 @@ Config::Config() {
rt_msaa_multiview_supported = false;
}
}
if (external_texture_supported) {
eglEGLImageTargetTexture2DOES = (PFNEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
if (eglEGLImageTargetTexture2DOES == nullptr) {
external_texture_supported = false;
}
}
#endif
force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
use_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
@ -218,7 +234,16 @@ Config::Config() {
//https://github.com/godotengine/godot/issues/92662#issuecomment-2161199477
//disable_particles_workaround = false;
}
} else if (rendering_device_name == "PowerVR Rogue GE8320") {
disable_transform_feedback_shader_cache = true;
}
if (OS::get_singleton()->get_current_rendering_driver_name() == "opengl3_angle") {
polyfill_half2float = false;
}
#ifdef WEB_ENABLED
polyfill_half2float = false;
#endif
}
Config::~Config() {

View file

@ -36,7 +36,6 @@
#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"
@ -45,6 +44,7 @@ typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint,
typedef void (*PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean);
typedef void (*PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei);
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei);
typedef void (*PFNEGLIMAGETARGETTEXTURE2DOESPROC)(GLenum, void *);
#endif
namespace GLES3 {
@ -62,6 +62,7 @@ public:
GLint max_texture_size = 0;
GLint max_viewport_size[2] = { 0, 0 };
GLint64 max_uniform_buffer_size = 0;
uint32_t max_shader_varyings = 0;
int64_t max_renderable_elements = 0;
int64_t max_renderable_lights = 0;
@ -72,6 +73,7 @@ public:
HashSet<String> extensions;
bool float_texture_supported = false;
bool float_texture_linear_supported = false;
bool s3tc_supported = false;
bool rgtc_supported = false;
bool bptc_supported = false;
@ -79,6 +81,7 @@ public:
bool astc_supported = false;
bool astc_hdr_supported = false;
bool astc_layered_supported = false;
bool srgb_framebuffer_supported = false;
bool force_vertex_shading = false;
@ -91,19 +94,27 @@ public:
bool rt_msaa_supported = false;
bool rt_msaa_multiview_supported = false;
bool multiview_supported = false;
bool external_texture_supported = false;
// Adreno 3XX compatibility
bool disable_particles_workaround = false; // set to 'true' to disable 'GPUParticles'
// Adreno 3XX compatibility.
bool disable_particles_workaround = false; // Set to 'true' to disable 'GPUParticles'.
bool flip_xy_workaround = false;
// PowerVR GE 8320 workaround.
bool disable_transform_feedback_shader_cache = false;
// ANGLE shader workaround.
bool polyfill_half2float = true;
#ifdef ANDROID_ENABLED
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr;
PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr;
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr;
PFNEGLIMAGETARGETTEXTURE2DOESPROC eglEGLImageTargetTexture2DOES = nullptr;
#endif
static Config *get_singleton() { return singleton; };
static Config *get_singleton() { return singleton; }
Config();
~Config();

View file

@ -33,7 +33,6 @@
#include "light_storage.h"
#include "../rasterizer_gles3.h"
#include "../rasterizer_scene_gles3.h"
#include "config.h"
#include "core/config/project_settings.h"
#include "texture_storage.h"
@ -213,6 +212,23 @@ void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) {
light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT);
}
void LightStorage::light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) {
Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL(light);
light->shadow_caster_mask = p_caster_mask;
light->version++;
light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT);
}
uint32_t LightStorage::light_get_shadow_caster_mask(RID p_light) const {
Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, 0);
return light->shadow_caster_mask;
}
void LightStorage::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) {
Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL(light);
@ -454,6 +470,13 @@ void LightStorage::reflection_probe_set_intensity(RID p_probe, float p_intensity
reflection_probe->intensity = p_intensity;
}
void LightStorage::reflection_probe_set_blend_distance(RID p_probe, float p_blend_distance) {
ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
ERR_FAIL_NULL(reflection_probe);
reflection_probe->blend_distance = p_blend_distance;
}
void LightStorage::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) {
ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
ERR_FAIL_NULL(reflection_probe);
@ -1046,6 +1069,9 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use
lightmap->light_texture = p_light;
lightmap->uses_spherical_harmonics = p_uses_spherical_haromics;
Vector3i light_texture_size = GLES3::TextureStorage::get_singleton()->texture_get_size(lightmap->light_texture);
lightmap->light_texture_size = Vector2i(light_texture_size.x, light_texture_size.y);
GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->light_texture);
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@ -1184,6 +1210,33 @@ float LightStorage::lightmap_get_probe_capture_update_speed() const {
return lightmap_probe_capture_update_speed;
}
void LightStorage::lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) {
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
ERR_FAIL_NULL(lightmap);
lightmap->shadow_texture = p_shadow;
GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->shadow_texture);
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
}
RS::ShadowmaskMode LightStorage::lightmap_get_shadowmask_mode(RID p_lightmap) {
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
ERR_FAIL_NULL_V(lightmap, RS::SHADOWMASK_MODE_NONE);
return lightmap->shadowmask_mode;
}
void LightStorage::lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) {
Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap);
ERR_FAIL_NULL(lightmap);
lightmap->shadowmask_mode = p_mode;
}
/* LIGHTMAP INSTANCE */
RID LightStorage::lightmap_instance_create(RID p_lightmap) {
@ -1385,7 +1438,7 @@ bool LightStorage::shadow_atlas_update_light(RID p_atlas, RID p_light_instance,
old_shadow = old_key & SHADOW_INDEX_MASK;
// Only re-allocate if a better option is available, and enough time has passed.
should_realloc = shadow_atlas->quadrants[old_quadrant].subdivision != (uint32_t)best_subdiv && (shadow_atlas->quadrants[old_quadrant].shadows[old_shadow].alloc_tick - tick > shadow_atlas_realloc_tolerance_msec);
should_realloc = shadow_atlas->quadrants[old_quadrant].subdivision != (uint32_t)best_subdiv && (tick - shadow_atlas->quadrants[old_quadrant].shadows[old_shadow].alloc_tick > shadow_atlas_realloc_tolerance_msec);
should_redraw = shadow_atlas->quadrants[old_quadrant].shadows[old_shadow].version != p_light_version;
if (!should_realloc) {
@ -1518,6 +1571,11 @@ bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_i
uint64_t min_pass = 0; // Pass of the existing one, try to use the least recently used one (LRU fashion).
for (int j = 0; j < sc; j++) {
if (sarr[j].owner_is_omni != is_omni) {
// Existing light instance type doesn't match new light instance type skip.
continue;
}
LightInstance *sli = light_instance_owner.get_or_null(sarr[j].owner);
if (!sli) {
// Found a released light instance.

View file

@ -38,9 +38,7 @@
#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"
@ -59,6 +57,7 @@ struct Light {
RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
uint32_t max_sdfgi_cascade = 2;
uint32_t cull_mask = 0xFFFFFFFF;
uint32_t shadow_caster_mask = 0xFFFFFFFF;
bool distance_fade = false;
real_t distance_fade_begin = 40.0;
real_t distance_fade_shadow = 50.0;
@ -117,6 +116,7 @@ struct ReflectionProbe {
RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE;
int resolution = 256;
float intensity = 1.0;
float blend_distance = 1.0;
RS::ReflectionProbeAmbientMode ambient_mode = RS::REFLECTION_PROBE_AMBIENT_ENVIRONMENT;
Color ambient_color;
float ambient_color_energy = 1.0;
@ -176,11 +176,14 @@ struct ReflectionProbeInstance {
struct Lightmap {
RID light_texture;
RID shadow_texture;
bool uses_spherical_harmonics = false;
bool interior = false;
AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
float baked_exposure = 1.0;
Vector2i light_texture_size;
int32_t array_index = -1; //unassigned
RS::ShadowmaskMode shadowmask_mode = RS::SHADOWMASK_MODE_NONE;
PackedVector3Array points;
PackedColorArray point_sh;
PackedInt32Array tetrahedra;
@ -202,7 +205,7 @@ struct LightmapInstance {
class LightStorage : public RendererLightStorage {
public:
enum ShadowAtlastQuadrant {
enum ShadowAtlastQuadrant : uint32_t {
QUADRANT_SHIFT = 27,
OMNI_LIGHT_FLAG = 1 << 26,
SHADOW_INDEX_MASK = OMNI_LIGHT_FLAG - 1,
@ -229,8 +232,6 @@ private:
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;
@ -304,8 +305,8 @@ public:
/* Light API */
Light *get_light(RID p_rid) { return light_owner.get_or_null(p_rid); };
bool owns_light(RID p_rid) { return light_owner.owns(p_rid); };
Light *get_light(RID p_rid) { return light_owner.get_or_null(p_rid); }
bool owns_light(RID p_rid) { return light_owner.owns(p_rid); }
void _light_initialize(RID p_rid, RS::LightType p_type);
@ -326,6 +327,8 @@ public:
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) override;
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override;
virtual void light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) override;
virtual uint32_t light_get_shadow_caster_mask(RID p_light) const override;
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override;
virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {}
@ -430,8 +433,8 @@ public:
/* LIGHT INSTANCE API */
LightInstance *get_light_instance(RID p_rid) { return light_instance_owner.get_or_null(p_rid); };
bool owns_light_instance(RID p_rid) { return light_instance_owner.owns(p_rid); };
LightInstance *get_light_instance(RID p_rid) { return light_instance_owner.get_or_null(p_rid); }
bool owns_light_instance(RID p_rid) { return light_instance_owner.owns(p_rid); }
virtual RID light_instance_create(RID p_light) override;
virtual void light_instance_free(RID p_light_instance) override;
@ -629,8 +632,8 @@ public:
/* PROBE API */
ReflectionProbe *get_reflection_probe(RID p_rid) { return reflection_probe_owner.get_or_null(p_rid); };
bool owns_reflection_probe(RID p_rid) { return reflection_probe_owner.owns(p_rid); };
ReflectionProbe *get_reflection_probe(RID p_rid) { return reflection_probe_owner.get_or_null(p_rid); }
bool owns_reflection_probe(RID p_rid) { return reflection_probe_owner.owns(p_rid); }
virtual RID reflection_probe_allocate() override;
virtual void reflection_probe_initialize(RID p_rid) override;
@ -638,6 +641,7 @@ public:
virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override;
virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) override;
virtual void reflection_probe_set_blend_distance(RID p_probe, float p_blend_distance) override;
virtual void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override;
virtual void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override;
virtual void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) override;
@ -711,8 +715,8 @@ public:
/* LIGHTMAP CAPTURE */
Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); };
bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); };
Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); }
bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); }
virtual RID lightmap_allocate() override;
virtual void lightmap_initialize(RID p_rid) override;
@ -733,23 +737,27 @@ public:
virtual void lightmap_set_probe_capture_update_speed(float p_speed) override;
virtual float lightmap_get_probe_capture_update_speed() const override;
virtual void lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) override;
virtual RS::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) override;
virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) override;
/* LIGHTMAP INSTANCE */
LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); };
bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); };
LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); }
bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); }
virtual RID lightmap_instance_create(RID p_lightmap) override;
virtual void lightmap_instance_free(RID p_lightmap) override;
virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override;
/* SHADOW ATLAS API */
bool owns_shadow_atlas(RID p_rid) { return shadow_atlas_owner.owns(p_rid); };
bool owns_shadow_atlas(RID p_rid) { return shadow_atlas_owner.owns(p_rid); }
virtual RID shadow_atlas_create() override;
virtual void shadow_atlas_free(RID p_atlas) override;
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;
virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_instance, float p_coverage, uint64_t p_light_version) override;
_FORCE_INLINE_ bool shadow_atlas_owns_light_instance(RID p_atlas, RID p_light_instance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
@ -875,7 +883,7 @@ public:
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 int get_directional_light_shadow_size(RID p_light_instance) override;
virtual void set_directional_shadow_count(int p_count) override;
Rect2i get_directional_shadow_rect();

View file

@ -348,7 +348,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_
}
}
_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) {
_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) {
switch (type) {
case ShaderLanguage::TYPE_BOOL: {
uint32_t *gui = (uint32_t *)data;
@ -572,7 +572,10 @@ void ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_t
Variant ShaderData::get_default_parameter(const StringName &p_parameter) const {
if (uniforms.has(p_parameter)) {
ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
Vector<ShaderLanguage::Scalar> default_value = uniform.default_value;
if (default_value.is_empty()) {
return ShaderLanguage::get_default_datatype_value(uniform.type, uniform.array_size, uniform.hint);
}
return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint);
}
return Variant();
@ -586,11 +589,7 @@ void ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const
if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) {
continue;
}
if (E.value.texture_order >= 0) {
filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000));
} else {
filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order));
}
filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.prop_order));
}
int uniform_count = filtered_uniforms.size();
sorter.sort(filtered_uniforms.ptr(), uniform_count);
@ -640,7 +639,7 @@ bool ShaderData::is_parameter_texture(const StringName &p_param) const {
return false;
}
return uniforms[p_param].texture_order >= 0;
return uniforms[p_param].is_texture();
}
///////////////////////////////////////////////////////////////////////////
@ -679,6 +678,7 @@ static const GLenum target_from_type[ShaderLanguage::TYPE_MAX] = {
GL_TEXTURE_3D, // TYPE_USAMPLER3D,
GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBE,
GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBEARRAY,
_GL_TEXTURE_EXTERNAL_OES, // TYPE_SAMPLEREXT
GL_TEXTURE_2D, // TYPE_STRUCT
};
@ -719,7 +719,7 @@ void MaterialData::update_uniform_buffer(const HashMap<StringName, ShaderLanguag
bool uses_global_buffer = false;
for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : p_uniforms) {
if (E.value.order < 0) {
if (E.value.is_texture()) {
continue; // texture, does not go here
}
@ -948,7 +948,10 @@ void MaterialData::update_textures(const HashMap<StringName, Variant> &p_paramet
}
} break;
case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: {
ERR_PRINT_ONCE("Type: SamplerCubeArray not supported in GL Compatibility rendering backend, please use another type.");
ERR_PRINT_ONCE("Type: SamplerCubeArray is not supported in the Compatibility renderer, please use another type.");
} break;
case ShaderLanguage::TYPE_SAMPLEREXT: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_EXT);
} break;
case ShaderLanguage::TYPE_ISAMPLER3D:
@ -1193,6 +1196,7 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["world_vertex_coords"] = "#define USE_WORLD_VERTEX_COORDS\n";
actions.global_buffer_array_variable = "global_shader_uniforms";
actions.instance_uniform_index_variable = "read_draw_data_instance_offset";
shaders.compiler_canvas.initialize(actions);
}
@ -1237,6 +1241,8 @@ MaterialStorage::MaterialStorage() {
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
@ -1276,7 +1282,6 @@ MaterialStorage::MaterialStorage() {
actions.renames["CUSTOM1"] = "custom1_attrib";
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz";
@ -1367,6 +1372,10 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
if (!GLES3::Config::get_singleton()->force_vertex_shading) {
// If forcing vertex shading, this will be defined already.
actions.render_mode_defines["vertex_lighting"] = "#define USE_VERTEX_LIGHTING\n";
}
actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
@ -1374,6 +1383,7 @@ MaterialStorage::MaterialStorage() {
actions.check_multiview_samplers = RasterizerGLES3::get_singleton()->is_xr_enabled();
actions.global_buffer_array_variable = "global_shader_uniforms";
actions.instance_uniform_index_variable = "instance_offset";
shaders.compiler_scene.initialize(actions);
}
@ -1385,7 +1395,7 @@ MaterialStorage::MaterialStorage() {
actions.renames["COLOR"] = "out_color";
actions.renames["VELOCITY"] = "out_velocity_flags.xyz";
//actions.renames["MASS"] = "mass"; ?
actions.renames["MASS"] = "mass";
actions.renames["ACTIVE"] = "particle_active";
actions.renames["RESTART"] = "restart";
actions.renames["CUSTOM"] = "out_custom";
@ -1952,6 +1962,7 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture
"sampler2DArray",
"sampler3D",
"samplerCube",
"samplerExternalOES"
};
RS::GlobalShaderParameterType gvtype = RS::GLOBAL_VAR_TYPE_MAX;
@ -2498,6 +2509,19 @@ bool MaterialStorage::material_casts_shadows(RID p_material) {
return true; //by default everything casts shadows
}
RS::CullMode MaterialStorage::material_get_cull_mode(RID p_material) const {
const GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_NULL_V(material, RS::CULL_MODE_DISABLED);
ERR_FAIL_NULL_V(material->shader, RS::CULL_MODE_DISABLED);
if (material->shader->data) {
SceneShaderData *data = dynamic_cast<SceneShaderData *>(material->shader->data);
if (data) {
return (RS::CullMode)data->cull_mode;
}
}
return RS::CULL_MODE_DISABLED;
}
void MaterialStorage::material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) {
GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_NULL(material);
@ -2664,7 +2688,11 @@ static void bind_uniforms_generic(const Vector<RID> &p_textures, const Vector<Sh
const ShaderCompiler::GeneratedCode::Texture &texture_uniform = texture_uniforms[texture_uniform_index];
if (texture) {
glActiveTexture(GL_TEXTURE0 + texture_offset + ti);
glBindTexture(target_from_type[texture_uniform.type], texture->tex_id);
GLenum target = target_from_type[texture_uniform.type];
if (target == _GL_TEXTURE_EXTERNAL_OES && !GLES3::Config::get_singleton()->external_texture_supported) {
target = GL_TEXTURE_2D;
}
glBindTexture(target, texture->tex_id);
if (texture->render_target) {
texture->render_target->used_in_frame = true;
}
@ -2896,7 +2924,7 @@ void SceneShaderData::set_code(const String &p_code) {
int blend_modei = BLEND_MODE_MIX;
int depth_testi = DEPTH_TEST_ENABLED;
int alpha_antialiasing_modei = ALPHA_ANTIALIASING_OFF;
int cull_modei = CULL_BACK;
int cull_modei = RS::CULL_MODE_BACK;
int depth_drawi = DEPTH_DRAW_OPAQUE;
ShaderCompiler::IdentifierActions actions;
@ -2919,9 +2947,9 @@ void SceneShaderData::set_code(const String &p_code) {
actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, CULL_DISABLED);
actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, CULL_FRONT);
actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, CULL_BACK);
actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, RS::CULL_MODE_DISABLED);
actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, RS::CULL_MODE_FRONT);
actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, RS::CULL_MODE_BACK);
actions.render_mode_flags["unshaded"] = &unshaded;
actions.render_mode_flags["wireframe"] = &wireframe;
@ -2979,7 +3007,7 @@ void SceneShaderData::set_code(const String &p_code) {
alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei);
depth_draw = DepthDraw(depth_drawi);
depth_test = DepthTest(depth_testi);
cull_mode = Cull(cull_modei);
cull_mode = RS::CullMode(cull_modei);
vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL; // We can always read vertices and normals.
vertex_input_mask |= uses_tangent << RS::ARRAY_TANGENT;
@ -3002,19 +3030,19 @@ void SceneShaderData::set_code(const String &p_code) {
#ifdef DEBUG_ENABLED
if (uses_particle_trails) {
WARN_PRINT_ONCE_ED("Particle trails are only available when using the Forward+ or Mobile rendering backends.");
WARN_PRINT_ONCE_ED("Particle trails are only available when using the Forward+ or Mobile renderers.");
}
if (uses_sss) {
WARN_PRINT_ONCE_ED("Sub-surface scattering is only available when using the Forward+ rendering backend.");
WARN_PRINT_ONCE_ED("Subsurface scattering is only available when using the Forward+ renderer.");
}
if (uses_transmittance) {
WARN_PRINT_ONCE_ED("Transmittance is only available when using the Forward+ rendering backend.");
WARN_PRINT_ONCE_ED("Transmittance is only available when using the Forward+ renderer.");
}
if (uses_normal_texture) {
WARN_PRINT_ONCE_ED("Reading from the normal-roughness texture is only available when using the Forward+ or Mobile rendering backends.");
WARN_PRINT_ONCE_ED("Reading from the normal-roughness texture is only available when using the Forward+ or Mobile renderers.");
}
#endif

View file

@ -33,10 +33,8 @@
#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"
@ -263,12 +261,6 @@ struct SceneShaderData : public ShaderData {
DEPTH_TEST_ENABLED
};
enum Cull {
CULL_DISABLED,
CULL_FRONT,
CULL_BACK
};
enum AlphaAntiAliasing {
ALPHA_ANTIALIASING_OFF,
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
@ -292,7 +284,7 @@ struct SceneShaderData : public ShaderData {
AlphaAntiAliasing alpha_antialiasing_mode;
DepthDraw depth_draw;
DepthTest depth_test;
Cull cull_mode;
RS::CullMode cull_mode;
bool uses_point_size;
bool uses_alpha;
@ -576,8 +568,8 @@ public:
/* SHADER API */
Shader *get_shader(RID p_rid) { return shader_owner.get_or_null(p_rid); };
bool owns_shader(RID p_rid) { return shader_owner.owns(p_rid); };
Shader *get_shader(RID p_rid) { return shader_owner.get_or_null(p_rid); }
bool owns_shader(RID p_rid) { return shader_owner.owns(p_rid); }
void _shader_make_dirty(Shader *p_shader);
@ -598,8 +590,8 @@ public:
/* MATERIAL API */
Material *get_material(RID p_rid) { return material_owner.get_or_null(p_rid); };
bool owns_material(RID p_rid) { return material_owner.owns(p_rid); };
Material *get_material(RID p_rid) { return material_owner.get_or_null(p_rid); }
bool owns_material(RID p_rid) { return material_owner.owns(p_rid); }
void _material_queue_update(Material *material, bool p_uniform, bool p_texture);
void _update_queued_materials();
@ -618,6 +610,7 @@ public:
virtual bool material_is_animated(RID p_material) override;
virtual bool material_casts_shadows(RID p_material) override;
virtual RS::CullMode material_get_cull_mode(RID p_material) const override;
virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override;

View file

@ -32,7 +32,6 @@
#include "mesh_storage.h"
#include "config.h"
#include "material_storage.h"
#include "texture_storage.h"
#include "utilities.h"
@ -301,7 +300,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
Vector<uint8_t> ir = new_surface.index_data;
wr = wf_indices.ptrw();
if (new_surface.vertex_count < (1 << 16)) {
if (new_surface.vertex_count <= 65536) {
// Read 16 bit indices.
const uint16_t *src_idx = (const uint16_t *)ir.ptr();
for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) {
@ -448,6 +447,72 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
mesh->material_cache.clear();
}
void MeshStorage::_mesh_surface_clear(Mesh *mesh, int p_surface) {
Mesh::Surface &s = *mesh->surfaces[p_surface];
if (s.vertex_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.vertex_buffer);
s.vertex_buffer = 0;
}
if (s.version_count != 0) {
for (uint32_t j = 0; j < s.version_count; j++) {
glDeleteVertexArrays(1, &s.versions[j].vertex_array);
s.versions[j].vertex_array = 0;
}
}
if (s.attribute_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.attribute_buffer);
s.attribute_buffer = 0;
}
if (s.skin_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.skin_buffer);
s.skin_buffer = 0;
}
if (s.index_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.index_buffer);
s.index_buffer = 0;
}
if (s.versions) {
memfree(s.versions); // reallocs, so free with memfree.
}
if (s.wireframe) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.wireframe->index_buffer);
memdelete(s.wireframe);
}
if (s.lod_count) {
for (uint32_t j = 0; j < s.lod_count; j++) {
if (s.lods[j].index_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.lods[j].index_buffer);
s.lods[j].index_buffer = 0;
}
}
memdelete_arr(s.lods);
}
if (mesh->blend_shape_count) {
for (uint32_t j = 0; j < mesh->blend_shape_count; j++) {
if (s.blend_shapes[j].vertex_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.blend_shapes[j].vertex_buffer);
s.blend_shapes[j].vertex_buffer = 0;
}
if (s.blend_shapes[j].vertex_array != 0) {
glDeleteVertexArrays(1, &s.blend_shapes[j].vertex_array);
s.blend_shapes[j].vertex_array = 0;
}
}
memdelete_arr(s.blend_shapes);
}
memdelete(mesh->surfaces[p_surface]);
}
int MeshStorage::mesh_get_blend_shape_count(RID p_mesh) const {
const Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL_V(mesh, -1);
@ -743,6 +808,7 @@ String MeshStorage::mesh_get_path(RID p_mesh) const {
}
void MeshStorage::mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) {
ERR_FAIL_COND_MSG(p_mesh == p_shadow_mesh, "Cannot set a mesh as its own shadow mesh.");
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL(mesh);
@ -771,69 +837,7 @@ void MeshStorage::mesh_clear(RID p_mesh) {
}
for (uint32_t i = 0; i < mesh->surface_count; i++) {
Mesh::Surface &s = *mesh->surfaces[i];
if (s.vertex_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.vertex_buffer);
s.vertex_buffer = 0;
}
if (s.version_count != 0) {
for (uint32_t j = 0; j < s.version_count; j++) {
glDeleteVertexArrays(1, &s.versions[j].vertex_array);
s.versions[j].vertex_array = 0;
}
}
if (s.attribute_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.attribute_buffer);
s.attribute_buffer = 0;
}
if (s.skin_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.skin_buffer);
s.skin_buffer = 0;
}
if (s.index_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.index_buffer);
s.index_buffer = 0;
}
if (s.versions) {
memfree(s.versions); //reallocs, so free with memfree.
}
if (s.wireframe) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.wireframe->index_buffer);
memdelete(s.wireframe);
}
if (s.lod_count) {
for (uint32_t j = 0; j < s.lod_count; j++) {
if (s.lods[j].index_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.lods[j].index_buffer);
s.lods[j].index_buffer = 0;
}
}
memdelete_arr(s.lods);
}
if (mesh->blend_shape_count) {
for (uint32_t j = 0; j < mesh->blend_shape_count; j++) {
if (s.blend_shapes[j].vertex_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(s.blend_shapes[j].vertex_buffer);
s.blend_shapes[j].vertex_buffer = 0;
}
if (s.blend_shapes[j].vertex_array != 0) {
glDeleteVertexArrays(1, &s.blend_shapes[j].vertex_array);
s.blend_shapes[j].vertex_array = 0;
}
}
memdelete_arr(s.blend_shapes);
}
memdelete(mesh->surfaces[i]);
_mesh_surface_clear(mesh, i);
}
if (mesh->surfaces) {
memfree(mesh->surfaces);
@ -843,6 +847,7 @@ void MeshStorage::mesh_clear(RID p_mesh) {
mesh->surface_count = 0;
mesh->material_cache.clear();
mesh->has_bone_weights = false;
mesh->aabb = AABB();
mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH);
for (Mesh *E : mesh->shadow_owners) {
@ -1032,6 +1037,56 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
v.input_mask = p_input_mask;
}
void MeshStorage::mesh_surface_remove(RID p_mesh, int p_surface) {
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL(mesh);
ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count);
// Clear instance data before mesh data.
for (MeshInstance *mi : mesh->instances) {
_mesh_instance_remove_surface(mi, p_surface);
}
_mesh_surface_clear(mesh, p_surface);
if ((uint32_t)p_surface < mesh->surface_count - 1) {
memmove(mesh->surfaces + p_surface, mesh->surfaces + p_surface + 1, sizeof(Mesh::Surface *) * (mesh->surface_count - (p_surface + 1)));
}
mesh->surfaces = (Mesh::Surface **)memrealloc(mesh->surfaces, sizeof(Mesh::Surface *) * (mesh->surface_count - 1));
--mesh->surface_count;
mesh->material_cache.clear();
mesh->skeleton_aabb_version = 0;
if (mesh->has_bone_weights) {
mesh->has_bone_weights = false;
for (uint32_t i = 0; i < mesh->surface_count; i++) {
if (mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) {
mesh->has_bone_weights = true;
break;
}
}
}
if (mesh->surface_count == 0) {
mesh->aabb = AABB();
} else {
mesh->aabb = mesh->surfaces[0]->aabb;
for (uint32_t i = 1; i < mesh->surface_count; i++) {
mesh->aabb.merge_with(mesh->surfaces[i]->aabb);
}
}
mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH);
for (Mesh *E : mesh->shadow_owners) {
Mesh *shadow_owner = E;
shadow_owner->shadow_mesh = RID();
shadow_owner->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH);
}
}
/* MESH INSTANCE API */
RID MeshStorage::mesh_instance_create(RID p_base) {
@ -1082,30 +1137,10 @@ void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int
}
void MeshStorage::_mesh_instance_clear(MeshInstance *mi) {
for (uint32_t i = 0; i < mi->surfaces.size(); i++) {
if (mi->surfaces[i].version_count != 0) {
for (uint32_t j = 0; j < mi->surfaces[i].version_count; j++) {
glDeleteVertexArrays(1, &mi->surfaces[i].versions[j].vertex_array);
mi->surfaces[i].versions[j].vertex_array = 0;
}
memfree(mi->surfaces[i].versions);
}
if (mi->surfaces[i].vertex_buffers[0] != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffers[0]);
GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffers[1]);
mi->surfaces[i].vertex_buffers[0] = 0;
mi->surfaces[i].vertex_buffers[1] = 0;
}
if (mi->surfaces[i].vertex_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffer);
mi->surfaces[i].vertex_buffer = 0;
}
while (mi->surfaces.size()) {
_mesh_instance_remove_surface(mi, mi->surfaces.size() - 1);
}
mi->surfaces.clear();
mi->blend_weights.clear();
mi->skeleton_version = 0;
mi->dirty = false;
}
void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface) {
@ -1158,6 +1193,39 @@ void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint3
mi->dirty = true;
}
void MeshStorage::_mesh_instance_remove_surface(MeshInstance *mi, int p_surface) {
MeshInstance::Surface &surface = mi->surfaces[p_surface];
if (surface.version_count != 0) {
for (uint32_t j = 0; j < surface.version_count; j++) {
glDeleteVertexArrays(1, &surface.versions[j].vertex_array);
surface.versions[j].vertex_array = 0;
}
memfree(surface.versions);
}
if (surface.vertex_buffers[0] != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(surface.vertex_buffers[0]);
GLES3::Utilities::get_singleton()->buffer_free_data(surface.vertex_buffers[1]);
surface.vertex_buffers[0] = 0;
surface.vertex_buffers[1] = 0;
}
if (surface.vertex_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(surface.vertex_buffer);
surface.vertex_buffer = 0;
}
mi->surfaces.remove_at(p_surface);
if (mi->surfaces.is_empty()) {
mi->blend_weights.clear();
mi->weights_dirty = false;
mi->skeleton_version = 0;
}
mi->dirty = true;
}
void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) {
MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance);
@ -1258,7 +1326,7 @@ void MeshStorage::update_mesh_instances() {
// Precompute base weight if using blend shapes.
float base_weight = 1.0;
if (mi->mesh->blend_shape_count && mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED) {
if (mi->surfaces.size() && mi->mesh->blend_shape_count && mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED) {
for (uint32_t i = 0; i < mi->mesh->blend_shape_count; i++) {
base_weight -= mi->blend_weights[i];
}
@ -1432,15 +1500,17 @@ void MeshStorage::update_mesh_instances() {
/* MULTIMESH API */
RID MeshStorage::multimesh_allocate() {
RID MeshStorage::_multimesh_allocate() {
return multimesh_owner.allocate_rid();
}
void MeshStorage::multimesh_initialize(RID p_rid) {
void MeshStorage::_multimesh_initialize(RID p_rid) {
multimesh_owner.initialize_rid(p_rid, MultiMesh());
}
void MeshStorage::multimesh_free(RID p_rid) {
void MeshStorage::_multimesh_free(RID p_rid) {
// Remove from interpolator.
_interpolation_data.notify_free_multimesh(p_rid);
_update_dirty_multimeshes();
multimesh_allocate_data(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D);
MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid);
@ -1448,7 +1518,7 @@ void MeshStorage::multimesh_free(RID p_rid) {
multimesh_owner.free(p_rid);
}
void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) {
void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data, bool p_use_indirect) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
@ -1495,13 +1565,13 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH);
}
int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const {
int MeshStorage::_multimesh_get_instance_count(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->instances;
}
void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
void MeshStorage::_multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
if (multimesh->mesh == p_mesh || p_mesh.is_null()) {
@ -1651,7 +1721,7 @@ void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p
multimesh->aabb = aabb;
}
void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) {
void MeshStorage::_multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1681,7 +1751,7 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index,
_multimesh_mark_dirty(multimesh, p_index, true);
}
void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {
void MeshStorage::_multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1707,7 +1777,7 @@ void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_ind
_multimesh_mark_dirty(multimesh, p_index, true);
}
void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
void MeshStorage::_multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1727,7 +1797,7 @@ void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, con
_multimesh_mark_dirty(multimesh, p_index, false);
}
void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
void MeshStorage::_multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_INDEX(p_index, multimesh->instances);
@ -1746,39 +1816,39 @@ void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_inde
_multimesh_mark_dirty(multimesh, p_index, false);
}
RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const {
RID MeshStorage::_multimesh_get_mesh(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, RID());
return multimesh->mesh;
}
void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
void MeshStorage::_multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
multimesh->custom_aabb = p_aabb;
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
AABB MeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const {
AABB MeshStorage::_multimesh_get_custom_aabb(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB());
return multimesh->custom_aabb;
}
AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const {
AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB());
if (multimesh->custom_aabb != AABB()) {
return multimesh->custom_aabb;
}
if (multimesh->aabb_dirty) {
const_cast<MeshStorage *>(this)->_update_dirty_multimeshes();
_update_dirty_multimeshes();
}
return multimesh->aabb;
}
Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const {
Transform3D MeshStorage::_multimesh_instance_get_transform(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Transform3D());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform3D());
@ -1809,7 +1879,7 @@ Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p
return t;
}
Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const {
Transform2D MeshStorage::_multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Transform2D());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform2D());
@ -1834,7 +1904,7 @@ Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, in
return t;
}
Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const {
Color MeshStorage::_multimesh_instance_get_color(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Color());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color());
@ -1858,7 +1928,7 @@ Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) co
return c;
}
Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
Color MeshStorage::_multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Color());
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color());
@ -1882,7 +1952,7 @@ Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_ind
return c;
}
void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
@ -1971,7 +2041,15 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
}
}
Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const {
RID MeshStorage::_multimesh_get_command_buffer_rd_rid(RID p_multimesh) const {
ERR_FAIL_V_MSG(RID(), "GLES3 does not implement indirect multimeshes.");
}
RID MeshStorage::_multimesh_get_buffer_rd_rid(RID p_multimesh) const {
ERR_FAIL_V_MSG(RID(), "GLES3 does not contain a Rid for the multimesh buffer.");
}
Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Vector<float>());
Vector<float> ret;
@ -2043,7 +2121,7 @@ Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const {
}
}
void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
void MeshStorage::_multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_COND(p_visible < -1 || p_visible > multimesh->instances);
@ -2065,12 +2143,19 @@ void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES);
}
int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const {
int MeshStorage::_multimesh_get_visible_instances(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->visible_instances;
}
MeshStorage::MultiMeshInterpolator *MeshStorage::_multimesh_get_interpolator(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V_MSG(multimesh, nullptr, "Multimesh not found: " + itos(p_multimesh.get_id()));
return &multimesh->interpolator;
}
void MeshStorage::_update_dirty_multimeshes() {
while (multimesh_dirty_list) {
MultiMesh *multimesh = multimesh_dirty_list;
@ -2191,7 +2276,7 @@ void MeshStorage::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_
glBindTexture(GL_TEXTURE_2D, 0);
GLES3::Utilities::get_singleton()->texture_allocated_data(skeleton->transforms_texture, skeleton->data.size() * sizeof(float), "Skeleton transforms texture");
memset(skeleton->data.ptrw(), 0, skeleton->data.size() * sizeof(float));
memset(skeleton->data.ptr(), 0, skeleton->data.size() * sizeof(float));
_skeleton_make_dirty(skeleton);
}
@ -2222,7 +2307,7 @@ void MeshStorage::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const
ERR_FAIL_INDEX(p_bone, skeleton->size);
ERR_FAIL_COND(skeleton->use_2d);
float *dataptr = skeleton->data.ptrw() + p_bone * 12;
float *dataptr = skeleton->data.ptr() + p_bone * 12;
dataptr[0] = p_transform.basis.rows[0][0];
dataptr[1] = p_transform.basis.rows[0][1];
@ -2274,7 +2359,7 @@ void MeshStorage::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, con
ERR_FAIL_INDEX(p_bone, skeleton->size);
ERR_FAIL_COND(!skeleton->use_2d);
float *dataptr = skeleton->data.ptrw() + p_bone * 8;
float *dataptr = skeleton->data.ptr() + p_bone * 8;
dataptr[0] = p_transform.columns[0][0];
dataptr[1] = p_transform.columns[1][0];

View file

@ -205,6 +205,8 @@ struct MultiMesh {
bool dirty = false;
MultiMesh *dirty_list = nullptr;
RendererMeshStorage::MultiMeshInterpolator interpolator;
Dependency dependency;
};
@ -212,7 +214,7 @@ struct Skeleton {
bool use_2d = false;
int size = 0;
int height = 0;
Vector<float> data;
LocalVector<float> data;
bool dirty = false;
Skeleton *dirty_list = nullptr;
@ -239,6 +241,7 @@ private:
mutable RID_Owner<Mesh, true> mesh_owner;
void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, MeshInstance::Surface *mis = nullptr);
void _mesh_surface_clear(Mesh *mesh, int p_surface);
/* Mesh Instance API */
@ -246,6 +249,7 @@ private:
void _mesh_instance_clear(MeshInstance *mi);
void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface);
void _mesh_instance_remove_surface(MeshInstance *mi, int p_surface);
void _blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface);
SelfList<MeshInstance>::List dirty_mesh_instance_weights;
SelfList<MeshInstance>::List dirty_mesh_instance_arrays;
@ -278,8 +282,8 @@ public:
/* MESH API */
Mesh *get_mesh(RID p_rid) { return mesh_owner.get_or_null(p_rid); };
bool owns_mesh(RID p_rid) { return mesh_owner.owns(p_rid); };
Mesh *get_mesh(RID p_rid) { return mesh_owner.get_or_null(p_rid); }
bool owns_mesh(RID p_rid) { return mesh_owner.owns(p_rid); }
virtual RID mesh_allocate() override;
virtual void mesh_initialize(RID p_rid) override;
@ -313,7 +317,9 @@ public:
virtual String mesh_get_path(RID p_mesh) const override;
virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override;
virtual void mesh_clear(RID p_mesh) override;
virtual void mesh_surface_remove(RID p_mesh, int p_surface) override;
_FORCE_INLINE_ const RID *mesh_get_surface_count_and_materials(RID p_mesh, uint32_t &r_surface_count) {
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
@ -441,8 +447,8 @@ public:
/* MESH INSTANCE API */
MeshInstance *get_mesh_instance(RID p_rid) { return mesh_instance_owner.get_or_null(p_rid); };
bool owns_mesh_instance(RID p_rid) { return mesh_instance_owner.owns(p_rid); };
MeshInstance *get_mesh_instance(RID p_rid) { return mesh_instance_owner.get_or_null(p_rid); }
bool owns_mesh_instance(RID p_rid) { return mesh_instance_owner.owns(p_rid); }
virtual RID mesh_instance_create(RID p_base) override;
virtual void mesh_instance_free(RID p_rid) override;
@ -490,35 +496,39 @@ public:
/* MULTIMESH API */
MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); };
bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); };
MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); }
bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); }
virtual RID multimesh_allocate() override;
virtual void multimesh_initialize(RID p_rid) override;
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 RID _multimesh_allocate() override;
virtual void _multimesh_initialize(RID p_rid) override;
virtual void _multimesh_free(RID p_rid) override;
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) override;
virtual int _multimesh_get_instance_count(RID p_multimesh) const override;
virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override;
virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override;
virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override;
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override;
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override;
virtual 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 RID _multimesh_get_mesh(RID p_multimesh) const override;
virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override;
virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override;
virtual AABB _multimesh_get_aabb(RID p_multimesh) override;
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual Vector<float> multimesh_get_buffer(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 RID _multimesh_get_command_buffer_rd_rid(RID p_multimesh) const override;
virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override;
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override;
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
virtual int multimesh_get_visible_instances(RID p_multimesh) const override;
virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
virtual int _multimesh_get_visible_instances(RID p_multimesh) const override;
virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override;
void _update_dirty_multimeshes();
@ -567,8 +577,8 @@ public:
/* SKELETON API */
Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); };
bool owns_skeleton(RID p_rid) { return skeleton_owner.owns(p_rid); };
Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); }
bool owns_skeleton(RID p_rid) { return skeleton_owner.owns(p_rid); }
virtual RID skeleton_allocate() override;
virtual void skeleton_initialize(RID p_rid) override;

View file

@ -38,7 +38,7 @@
#include "texture_storage.h"
#include "utilities.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/rendering/rendering_server_globals.h"
using namespace GLES3;
@ -223,6 +223,19 @@ void ParticlesStorage::particles_set_pre_process_time(RID p_particles, double p_
ERR_FAIL_NULL(particles);
particles->pre_process_time = p_time;
}
void ParticlesStorage::particles_request_process_time(RID p_particles, real_t p_request_process_time) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
particles->request_process_time = p_request_process_time;
}
void ParticlesStorage::particles_set_seed(RID p_particles, uint32_t p_seed) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
particles->random_seed = p_seed;
}
void ParticlesStorage::particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
@ -287,13 +300,13 @@ void ParticlesStorage::particles_set_fractional_delta(RID p_particles, bool p_en
void ParticlesStorage::particles_set_trails(RID p_particles, bool p_enable, double p_length) {
if (p_enable) {
WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support particle trails.");
WARN_PRINT_ONCE_ED("The Compatibility renderer does not support particle trails.");
}
}
void ParticlesStorage::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) {
if (p_bind_poses.size() != 0) {
WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support particle trails.");
WARN_PRINT_ONCE_ED("The Compatibility renderer does not support particle trails.");
}
}
@ -357,12 +370,12 @@ void ParticlesStorage::particles_restart(RID p_particles) {
void ParticlesStorage::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) {
if (p_subemitter_particles.is_valid()) {
WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support particle sub-emitters.");
WARN_PRINT_ONCE_ED("The Compatibility renderer does not support particle sub-emitters.");
}
}
void ParticlesStorage::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) {
WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support manually emitting particles.");
WARN_PRINT_ONCE_ED("The Compatibility renderer does not support manually emitting particles.");
}
void ParticlesStorage::particles_request_process(RID p_particles) {
@ -507,7 +520,6 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
if (p_particles->clear) {
p_particles->cycle_number = 0;
p_particles->random_seed = Math::rand();
} else if (new_phase < p_particles->phase) {
if (p_particles->one_shot) {
p_particles->emitting = false;
@ -645,7 +657,7 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
attr.extents[2] = extents.z;
} break;
case RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT: {
WARN_PRINT_ONCE_ED("Vector field particle attractors are not available in the GL Compatibility rendering backend.");
WARN_PRINT_ONCE_ED("Vector field particle attractors are not available in the Compatibility renderer.");
} break;
default: {
}
@ -678,7 +690,7 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
col.extents[2] = extents.z;
} break;
case RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE: {
WARN_PRINT_ONCE_ED("SDF Particle Colliders are not available in the GL Compatibility rendering backend.");
WARN_PRINT_ONCE_ED("SDF Particle Colliders are not available in the Compatibility renderer.");
} break;
case RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE: {
if (collision_heightmap_texture != 0) { //already taken
@ -1136,6 +1148,24 @@ void ParticlesStorage::update_particles() {
}
}
if (particles->request_process_time > 0.0) {
double frame_time;
if (fixed_fps > 0) {
frame_time = 1.0 / fixed_fps;
} else {
frame_time = 1.0 / 30.0;
}
float tmp_scale = particles->speed_scale;
particles->speed_scale = 1.0;
double todo = particles->request_process_time;
while (todo >= 0) {
_particles_process(particles, frame_time);
todo -= frame_time;
}
particles->speed_scale = tmp_scale;
particles->request_process_time = 0.0;
}
// Copy particles to instance buffer and pack Color/Custom.
// We don't have camera information here, so don't copy here if we need camera information for view depth or align mode.
if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
@ -1341,7 +1371,7 @@ void ParticlesStorage::particles_collision_set_attractor_attenuation(RID p_parti
}
void ParticlesStorage::particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) {
WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support SDF collisions in 3D particle shaders");
WARN_PRINT_ONCE_ED("The Compatibility renderer does not support SDF collisions in 3D particle shaders");
}
void ParticlesStorage::particles_collision_height_field_update(RID p_particles_collision) {
@ -1402,6 +1432,18 @@ bool ParticlesStorage::particles_collision_is_heightfield(RID p_particles_collis
return particles_collision->type == RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE;
}
uint32_t ParticlesStorage::particles_collision_get_height_field_mask(RID p_particles_collision) const {
const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision);
ERR_FAIL_NULL_V(particles_collision, false);
return particles_collision->heightfield_mask;
}
void ParticlesStorage::particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) {
ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision);
ERR_FAIL_NULL(particles_collision);
particles_collision->heightfield_mask = p_heightfield_mask;
}
Dependency *ParticlesStorage::particles_collision_get_dependency(RID p_particles_collision) const {
ParticlesCollision *pc = particles_collision_owner.get_or_null(p_particles_collision);
ERR_FAIL_NULL_V(pc, nullptr);

View file

@ -33,7 +33,6 @@
#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"
@ -158,6 +157,7 @@ private:
int amount = 0;
double lifetime = 1.0;
double pre_process_time = 0.0;
real_t request_process_time = 0.0;
real_t explosiveness = 0.0;
real_t randomness = 0.0;
bool restart_request = false;
@ -247,6 +247,7 @@ private:
Particles() :
update_list(this) {
random_seed = Math::rand();
}
};
@ -286,6 +287,7 @@ private:
GLuint heightfield_texture = 0;
GLuint heightfield_fb = 0;
Size2i heightfield_fb_size;
uint32_t heightfield_mask = (1 << 20) - 1;
RS::ParticlesCollisionHeightfieldResolution heightfield_resolution = RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024;
@ -326,6 +328,7 @@ public:
virtual void particles_set_lifetime(RID p_particles, double p_lifetime) override;
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) override;
virtual void particles_set_pre_process_time(RID p_particles, double p_time) override;
virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time) override;
virtual void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override;
@ -341,6 +344,7 @@ public:
virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) override;
virtual void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override;
virtual void particles_set_seed(RID p_particles, uint32_t p_seed) override;
virtual void particles_set_trails(RID p_particles, bool p_enable, double p_length) override;
virtual void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) override;
@ -396,7 +400,7 @@ public:
_FORCE_INLINE_ bool particles_has_collision(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, 0);
ERR_FAIL_NULL_V(particles, false);
return particles->has_collision_cache;
}
@ -431,6 +435,8 @@ public:
Vector3 particles_collision_get_extents(RID p_particles_collision) const;
virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override;
GLuint particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const;
virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const override;
virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) override;
_FORCE_INLINE_ Size2i particles_collision_get_heightfield_size(RID p_particles_collision) const {
ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision);

View file

@ -138,6 +138,7 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p
scaling_3d_mode = p_config->get_scaling_3d_mode();
//fsr_sharpness = p_config->get_fsr_sharpness();
//texture_mipmap_bias = p_config->get_texture_mipmap_bias();
//anisotropic_filtering_level = p_config->get_anisotropic_filtering_level();
render_target = p_config->get_render_target();
msaa3d.mode = p_config->get_msaa_3d();
//screen_space_aa = p_config->get_screen_space_aa();

View file

@ -103,9 +103,10 @@ public:
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{};
virtual void set_anisotropic_filtering_level(RS::ViewportAnisotropicFiltering p_anisotropic_filtering_level) override {}
virtual void set_fsr_sharpness(float p_fsr_sharpness) override {}
virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override {}
virtual void set_use_debanding(bool p_use_debanding) override {}
void set_apply_color_adjustments_in_post(bool p_apply_in_post);
void free_render_buffer_data();

View file

@ -152,6 +152,11 @@ TextureStorage::TextureStorage() {
texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_ANISO], image);
}
{
default_gl_textures[DEFAULT_GL_TEXTURE_EXT] = texture_allocate();
texture_external_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_EXT], 1, 1, 0);
}
{
unsigned char pixel_data[4 * 4 * 4];
for (int i = 0; i < 16; i++) {
@ -225,6 +230,32 @@ TextureStorage::TextureStorage() {
sdf_shader.shader_version = sdf_shader.shader.version_create();
}
// Initialize texture placeholder data for the `texture_*_placeholder_initialize()` methods.
constexpr int placeholder_size = 4;
texture_2d_placeholder = Image::create_empty(placeholder_size, placeholder_size, false, Image::FORMAT_RGBA8);
// Draw a magenta/black checkerboard pattern.
for (int i = 0; i < placeholder_size * placeholder_size; i++) {
const int x = i % placeholder_size;
const int y = i / placeholder_size;
texture_2d_placeholder->set_pixel(x, y, (x + y) % 2 == 0 ? Color(1, 0, 1) : Color(0, 0, 0));
}
texture_2d_array_placeholder.push_back(texture_2d_placeholder);
for (int i = 0; i < 6; i++) {
cubemap_placeholder.push_back(texture_2d_placeholder);
}
Ref<Image> texture_2d_placeholder_rotated;
texture_2d_placeholder_rotated.instantiate();
texture_2d_placeholder_rotated->copy_from(texture_2d_placeholder);
texture_2d_placeholder_rotated->rotate_90(CLOCKWISE);
for (int i = 0; i < 4; i++) {
// Alternate checkerboard pattern on odd layers (by using a copy that is rotated 90 degrees).
texture_3d_placeholder.push_back(i % 2 == 0 ? texture_2d_placeholder : texture_2d_placeholder_rotated);
}
#ifdef GL_API_ENABLED
if (RasterizerGLES3::is_gles_over_gl()) {
glEnable(GL_PROGRAM_POINT_SIZE);
@ -246,11 +277,6 @@ TextureStorage::~TextureStorage() {
sdf_shader.shader.version_free(sdf_shader.shader_version);
}
//TODO, move back to storage
bool TextureStorage::can_create_resources_async() const {
return false;
}
/* Canvas Texture API */
RID TextureStorage::canvas_texture_allocate() {
@ -308,15 +334,8 @@ void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS:
/* Texture API */
Ref<Image> TextureStorage::_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 {
static inline Error _get_gl_uncompressed_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) {
Config *config = Config::get_singleton();
r_gl_format = 0;
Ref<Image> image = p_image;
r_compressed = false;
r_real_format = p_format;
bool need_decompress = false;
bool decompress_ra_to_rg = false;
switch (p_format) {
case Image::FORMAT_L8: {
@ -345,55 +364,95 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_internal_format = GL_R8;
r_gl_format = GL_RED;
r_gl_type = GL_UNSIGNED_BYTE;
} break;
case Image::FORMAT_RG8: {
r_gl_internal_format = GL_RG8;
r_gl_format = GL_RG;
r_gl_type = GL_UNSIGNED_BYTE;
} break;
case Image::FORMAT_RGB8: {
r_gl_internal_format = GL_RGB8;
r_gl_format = GL_RGB;
r_gl_type = GL_UNSIGNED_BYTE;
} break;
case Image::FORMAT_RGBA8: {
r_gl_format = GL_RGBA;
r_gl_internal_format = GL_RGBA8;
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
} break;
case Image::FORMAT_RGBA4444: {
r_gl_internal_format = GL_RGBA4;
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_SHORT_4_4_4_4;
} break;
case Image::FORMAT_RGB565: {
r_gl_internal_format = GL_RGB565;
r_gl_format = GL_RGB;
r_gl_type = GL_UNSIGNED_SHORT_5_6_5;
} break;
case Image::FORMAT_RF: {
r_gl_internal_format = GL_R32F;
r_gl_format = GL_RED;
r_gl_type = GL_FLOAT;
if (config->float_texture_linear_supported) {
r_gl_internal_format = GL_R32F;
r_gl_format = GL_RED;
r_gl_type = GL_FLOAT;
} else {
ERR_PRINT("R32 float texture not supported, converting to R16.");
if (p_image.is_valid()) {
p_image->convert(Image::FORMAT_RH);
}
r_real_format = Image::FORMAT_RH;
r_gl_internal_format = GL_R16F;
r_gl_format = GL_RED;
r_gl_type = GL_HALF_FLOAT;
}
} break;
case Image::FORMAT_RGF: {
r_gl_internal_format = GL_RG32F;
r_gl_format = GL_RG;
r_gl_type = GL_FLOAT;
if (config->float_texture_linear_supported) {
r_gl_internal_format = GL_RG32F;
r_gl_format = GL_RG;
r_gl_type = GL_FLOAT;
} else {
ERR_PRINT("RG32 float texture not supported, converting to RG16.");
if (p_image.is_valid()) {
p_image->convert(Image::FORMAT_RGH);
}
r_real_format = Image::FORMAT_RGH;
r_gl_internal_format = GL_RG16F;
r_gl_format = GL_RG;
r_gl_type = GL_HALF_FLOAT;
}
} break;
case Image::FORMAT_RGBF: {
r_gl_internal_format = GL_RGB32F;
r_gl_format = GL_RGB;
r_gl_type = GL_FLOAT;
if (config->float_texture_linear_supported) {
r_gl_internal_format = GL_RGB32F;
r_gl_format = GL_RGB;
r_gl_type = GL_FLOAT;
} else {
ERR_PRINT("RGB32 float texture not supported, converting to RGB16.");
if (p_image.is_valid()) {
p_image->convert(Image::FORMAT_RGBH);
}
r_real_format = Image::FORMAT_RGBH;
r_gl_internal_format = GL_RGB16F;
r_gl_format = GL_RGB;
r_gl_type = GL_HALF_FLOAT;
}
} break;
case Image::FORMAT_RGBAF: {
r_gl_internal_format = GL_RGBA32F;
r_gl_format = GL_RGBA;
r_gl_type = GL_FLOAT;
if (config->float_texture_linear_supported) {
r_gl_internal_format = GL_RGBA32F;
r_gl_format = GL_RGBA;
r_gl_type = GL_FLOAT;
} else {
ERR_PRINT("RGBA32 float texture not supported, converting to RGBA16.");
if (p_image.is_valid()) {
p_image->convert(Image::FORMAT_RGBAH);
}
r_real_format = Image::FORMAT_RGBAH;
r_gl_internal_format = GL_RGBA16F;
r_gl_format = GL_RGBA;
r_gl_type = GL_HALF_FLOAT;
}
} break;
case Image::FORMAT_RH: {
r_gl_internal_format = GL_R16F;
@ -404,26 +463,48 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_internal_format = GL_RG16F;
r_gl_format = GL_RG;
r_gl_type = GL_HALF_FLOAT;
} break;
case Image::FORMAT_RGBH: {
r_gl_internal_format = GL_RGB16F;
r_gl_format = GL_RGB;
r_gl_type = GL_HALF_FLOAT;
} break;
case Image::FORMAT_RGBAH: {
r_gl_internal_format = GL_RGBA16F;
r_gl_format = GL_RGBA;
r_gl_type = GL_HALF_FLOAT;
} break;
case Image::FORMAT_RGBE9995: {
r_gl_internal_format = GL_RGB9_E5;
r_gl_format = GL_RGB;
r_gl_type = GL_UNSIGNED_INT_5_9_9_9_REV;
} break;
default: {
return ERR_UNAVAILABLE;
}
}
return OK;
}
Ref<Image> TextureStorage::_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 {
Config *config = Config::get_singleton();
r_gl_format = 0;
Ref<Image> image = p_image;
r_compressed = false;
r_real_format = p_format;
if (!Image::is_format_compressed(p_format)) {
Error err = _get_gl_uncompressed_format(p_image, p_format, r_real_format, r_gl_format, r_gl_internal_format, r_gl_type);
ERR_FAIL_COND_V_MSG(err != OK, Ref<Image>(), vformat("The image format %d is not supported by the Compatibility renderer.", p_format));
return p_image;
}
// For compressed images, some formats may not be supported by the current device and will require decompression.
bool need_decompress = false;
bool decompress_ra_to_rg = false;
switch (p_format) {
case Image::FORMAT_DXT1: {
if (config->s3tc_supported) {
r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT;
@ -510,7 +591,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RED;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -521,7 +601,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RED;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -532,7 +611,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RG;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -543,7 +621,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RG;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -555,7 +632,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RGB;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -566,7 +642,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -577,7 +652,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -616,7 +690,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -627,7 +700,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -638,7 +710,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
@ -649,56 +720,31 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
} break;
default: {
ERR_FAIL_V_MSG(Ref<Image>(), "The image format " + itos(p_format) + " is not supported by the GL Compatibility rendering backend.");
ERR_FAIL_V_MSG(Ref<Image>(), vformat("The image format %d is not supported by the Compatibility renderer.", p_format));
}
}
if (need_decompress || p_force_decompress) {
if (!image.is_null()) {
if (image.is_valid()) {
image = image->duplicate();
image->decompress();
ERR_FAIL_COND_V(image->is_compressed(), image);
if (decompress_ra_to_rg) {
image->convert_ra_rgba8_to_rg();
image->convert(Image::FORMAT_RG8);
}
switch (image->get_format()) {
case Image::FORMAT_RG8: {
r_gl_format = GL_RG;
r_gl_internal_format = GL_RG8;
r_gl_type = GL_UNSIGNED_BYTE;
r_real_format = Image::FORMAT_RG8;
r_compressed = false;
} break;
case Image::FORMAT_RGB8: {
r_gl_format = GL_RGB;
r_gl_internal_format = GL_RGB;
r_gl_type = GL_UNSIGNED_BYTE;
r_real_format = Image::FORMAT_RGB8;
r_compressed = false;
} break;
case Image::FORMAT_RGBA8: {
r_gl_format = GL_RGBA;
r_gl_internal_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_real_format = Image::FORMAT_RGBA8;
r_compressed = false;
} break;
default: {
image->convert(Image::FORMAT_RGBA8);
r_gl_format = GL_RGBA;
r_gl_internal_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_real_format = Image::FORMAT_RGBA8;
r_compressed = false;
} break;
}
Error err = _get_gl_uncompressed_format(image, image->get_format(), r_real_format, r_gl_format, r_gl_internal_format, r_gl_type);
ERR_FAIL_COND_V_MSG(err != OK, Ref<Image>(), vformat("The image format %d is not supported by the Compatibility renderer.", image->get_format()));
r_real_format = image->get_format();
r_compressed = false;
}
return image;
@ -729,7 +775,7 @@ void TextureStorage::texture_free(RID p_texture) {
}
}
} else {
must_free_data = t->tex_id != 0 && !t->is_external;
must_free_data = t->tex_id != 0 && !t->is_from_native_handle;
}
if (must_free_data) {
GLES3::Utilities::get_singleton()->texture_free_data(t->tex_id);
@ -769,11 +815,53 @@ void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_im
texture_set_data(p_texture, p_image);
}
void TextureStorage::texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) {
Texture texture;
texture.active = true;
texture.alloc_width = texture.width = p_width;
texture.alloc_height = texture.height = p_height;
texture.real_format = texture.format = Image::FORMAT_RGB8;
texture.type = Texture::TYPE_2D;
if (GLES3::Config::get_singleton()->external_texture_supported) {
texture.target = _GL_TEXTURE_EXTERNAL_OES;
} else {
texture.target = GL_TEXTURE_2D;
}
glGenTextures(1, &texture.tex_id);
glBindTexture(texture.target, texture.tex_id);
#ifdef ANDROID_ENABLED
if (texture.target == _GL_TEXTURE_EXTERNAL_OES) {
if (p_external_buffer) {
GLES3::Config::get_singleton()->eglEGLImageTargetTexture2DOES(_GL_TEXTURE_EXTERNAL_OES, reinterpret_cast<void *>(p_external_buffer));
}
texture.total_data_size = 0;
} else
#endif
{
// If external textures aren't supported, allocate an empty 1x1 texture.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
texture.total_data_size = 3;
}
glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLES3::Utilities::get_singleton()->texture_allocated_data(texture.tex_id, texture.total_data_size, "Texture External");
texture_owner.initialize_rid(p_texture, texture);
glBindTexture(texture.target, 0);
}
void TextureStorage::texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) {
ERR_FAIL_COND(p_layers.is_empty());
ERR_FAIL_COND(p_layered_type == RS::TEXTURE_LAYERED_CUBEMAP && p_layers.size() != 6);
ERR_FAIL_COND_MSG(p_layered_type == RS::TEXTURE_LAYERED_CUBEMAP_ARRAY, "Cubemap Arrays are not supported in the GL Compatibility backend.");
ERR_FAIL_COND_MSG(p_layered_type == RS::TEXTURE_LAYERED_CUBEMAP_ARRAY, "Cubemap Arrays are not supported in the Compatibility renderer.");
const Ref<Image> &image = p_layers[0];
{
@ -874,26 +962,28 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) {
texture_owner.initialize_rid(p_texture, proxy_tex);
}
RID TextureStorage::texture_create_external(GLES3::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) {
RID TextureStorage::texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type) {
Texture texture;
texture.active = true;
texture.is_external = true;
texture.type = p_type;
texture.is_from_native_handle = true;
switch (p_type) {
case Texture::TYPE_2D: {
case RS::TEXTURE_TYPE_2D: {
texture.type = Texture::TYPE_2D;
texture.target = GL_TEXTURE_2D;
} break;
case Texture::TYPE_3D: {
case RS::TEXTURE_TYPE_3D: {
texture.type = Texture::TYPE_3D;
texture.target = GL_TEXTURE_3D;
} break;
case Texture::TYPE_LAYERED: {
case RS::TEXTURE_TYPE_LAYERED: {
texture.type = Texture::TYPE_LAYERED;
texture.target = GL_TEXTURE_2D_ARRAY;
} break;
}
texture.real_format = texture.format = p_format;
texture.tex_id = p_image;
texture.tex_id = p_native_handle;
texture.alloc_width = texture.width = p_width;
texture.alloc_height = texture.height = p_height;
texture.depth = p_depth;
@ -928,6 +1018,22 @@ void TextureStorage::texture_3d_update(RID p_texture, const Vector<Ref<Image>> &
GLES3::Utilities::get_singleton()->texture_resize_data(tex->tex_id, tex->total_data_size);
}
void TextureStorage::texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) {
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL(tex);
tex->alloc_width = tex->width = p_width;
tex->alloc_height = tex->height = p_height;
#ifdef ANDROID_ENABLED
if (tex->target == _GL_TEXTURE_EXTERNAL_OES && p_external_buffer) {
glBindTexture(_GL_TEXTURE_EXTERNAL_OES, tex->tex_id);
GLES3::Config::get_singleton()->eglEGLImageTargetTexture2DOES(_GL_TEXTURE_EXTERNAL_OES, reinterpret_cast<void *>(p_external_buffer));
glBindTexture(_GL_TEXTURE_EXTERNAL_OES, 0);
}
#endif
}
void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL(tex);
@ -954,46 +1060,19 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
}
void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
//this could be better optimized to reuse an existing image , done this way
//for now to get it working
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
texture_2d_initialize(p_texture, image);
texture_2d_initialize(p_texture, texture_2d_placeholder);
}
void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) {
//this could be better optimized to reuse an existing image , done this way
//for now to get it working
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
Vector<Ref<Image>> images;
void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS::TextureLayeredType p_layered_type) {
if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) {
images.push_back(image);
texture_2d_layered_initialize(p_texture, texture_2d_array_placeholder, p_layered_type);
} else {
//cube
for (int i = 0; i < 6; i++) {
images.push_back(image);
}
texture_2d_layered_initialize(p_texture, cubemap_placeholder, p_layered_type);
}
texture_2d_layered_initialize(p_texture, images, p_layered_type);
}
void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) {
//this could be better optimized to reuse an existing image , done this way
//for now to get it working
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
Vector<Ref<Image>> images;
//cube
for (int i = 0; i < 4; i++) {
images.push_back(image);
}
texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, images);
texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, texture_3d_placeholder);
}
Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
@ -1013,7 +1092,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
// It also allows for reading compressed textures, mipmaps, and more formats.
Vector<uint8_t> data;
int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->real_format, texture->mipmaps > 1);
int64_t data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->real_format, texture->mipmaps > 1);
data.resize(data_size * 2); // Add some memory at the end, just in case for buggy drivers.
uint8_t *w = data.ptrw();
@ -1025,7 +1104,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
for (int i = 0; i < texture->mipmaps; i++) {
int ofs = Image::get_image_mipmap_offset(texture->alloc_width, texture->alloc_height, texture->real_format, i);
int64_t ofs = Image::get_image_mipmap_offset(texture->alloc_width, texture->alloc_height, texture->real_format, i);
if (texture->compressed) {
glPixelStorei(GL_PACK_ALIGNMENT, 4);
@ -1039,9 +1118,13 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
data.resize(data_size);
ERR_FAIL_COND_V(data.is_empty(), Ref<Image>());
image = Image::create_from_data(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data);
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
if (texture->format != texture->real_format) {
image = Image::create_from_data(texture->alloc_width, texture->alloc_height, texture->mipmaps > 1, texture->real_format, data);
if (image->is_empty()) {
const String &path_str = texture->path.is_empty() ? "with no path" : vformat("with path '%s'", texture->path);
ERR_FAIL_V_MSG(Ref<Image>(), vformat("Texture %s has no data.", path_str));
}
if (texture->format != texture->real_format && !Image::is_format_compressed(texture->real_format)) {
image->convert(texture->format);
}
}
@ -1051,7 +1134,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
Vector<uint8_t> data;
// On web and mobile we always read an RGBA8 image with no mipmaps.
int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false);
int64_t data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false);
data.resize(data_size * 2); // Add some memory at the end, just in case for buggy drivers.
uint8_t *w = data.ptrw();
@ -1095,10 +1178,13 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
data.resize(data_size);
ERR_FAIL_COND_V(data.is_empty(), Ref<Image>());
image = Image::create_from_data(texture->width, texture->height, false, Image::FORMAT_RGBA8, data);
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
image = Image::create_from_data(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data);
if (image->is_empty()) {
const String &path_str = texture->path.is_empty() ? "with no path" : vformat("with path '%s'", texture->path);
ERR_FAIL_V_MSG(Ref<Image>(), vformat("Texture %s has no data.", path_str));
}
if (texture->format != Image::FORMAT_RGBA8) {
if (texture->format != Image::FORMAT_RGBA8 && !Image::is_format_compressed(texture->format)) {
image->convert(texture->format);
}
@ -1123,7 +1209,7 @@ Ref<Image> TextureStorage::texture_2d_layer_get(RID p_texture, int p_layer) cons
Vector<uint8_t> data;
int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false);
int64_t data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false);
data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers
uint8_t *w = data.ptrw();
@ -1168,9 +1254,12 @@ Ref<Image> TextureStorage::texture_2d_layer_get(RID p_texture, int p_layer) cons
ERR_FAIL_COND_V(data.is_empty(), Ref<Image>());
Ref<Image> image = Image::create_from_data(texture->width, texture->height, false, Image::FORMAT_RGBA8, data);
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
if (image->is_empty()) {
const String &path_str = texture->path.is_empty() ? "with no path" : vformat("with path '%s'", texture->path);
ERR_FAIL_V_MSG(Ref<Image>(), vformat("Texture %s has no data.", path_str));
}
if (texture->format != Image::FORMAT_RGBA8) {
if (texture->format != Image::FORMAT_RGBA8 && !Image::is_format_compressed(texture->format)) {
image->convert(texture->format);
}
@ -1192,7 +1281,7 @@ Vector<Ref<Image>> TextureStorage::_texture_3d_read_framebuffer(GLES3::Texture *
int depth = p_texture->depth;
for (int mipmap_level = 0; mipmap_level < p_texture->mipmaps; mipmap_level++) {
int data_size = Image::get_image_data_size(width, height, Image::FORMAT_RGBA8, false);
int64_t data_size = Image::get_image_data_size(width, height, Image::FORMAT_RGBA8, false);
glViewport(0, 0, width, height);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
@ -1211,7 +1300,7 @@ Vector<Ref<Image>> TextureStorage::_texture_3d_read_framebuffer(GLES3::Texture *
Ref<Image> img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, data);
ERR_FAIL_COND_V(img->is_empty(), Vector<Ref<Image>>());
if (p_texture->format != Image::FORMAT_RGBA8) {
if (p_texture->format != Image::FORMAT_RGBA8 && !Image::is_format_compressed(p_texture->format)) {
img->convert(p_texture->format);
}
@ -1389,8 +1478,22 @@ void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) {
tinfo.format = t->format;
tinfo.width = t->alloc_width;
tinfo.height = t->alloc_height;
tinfo.depth = t->depth;
tinfo.bytes = t->total_data_size;
switch (t->type) {
case Texture::TYPE_3D:
tinfo.depth = t->depth;
break;
case Texture::TYPE_LAYERED:
tinfo.depth = t->layers;
break;
default:
tinfo.depth = 0;
break;
}
r_info->push_back(tinfo);
}
}
@ -1452,8 +1555,17 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image,
GLenum internal_format;
bool compressed = false;
bool needs_decompress = texture->resize_to_po2;
// Support for RGTC-compressed Texture Arrays isn't mandated by GLES3/WebGL.
if (!RasterizerGLES3::is_gles_over_gl() && texture->target == GL_TEXTURE_2D_ARRAY) {
if (p_image->get_format() == Image::FORMAT_RGTC_R || p_image->get_format() == Image::FORMAT_RGTC_RG) {
needs_decompress = true;
}
}
Image::Format real_format;
Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), real_format, format, internal_format, type, compressed, texture->resize_to_po2);
Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), real_format, format, internal_format, type, compressed, needs_decompress);
ERR_FAIL_COND(img.is_null());
if (texture->resize_to_po2) {
if (p_image->is_compressed()) {
@ -1497,11 +1609,9 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image,
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
if (texture->target == GL_TEXTURE_2D_ARRAY) {
if (p_initialize) {
glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0,
size * texture->layers, &read[ofs]);
} else {
glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]);
glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0, size * texture->layers, nullptr);
}
glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]);
} else {
glCompressedTexImage2D(blit_target, i, internal_format, w, h, 0, size, &read[ofs]);
}
@ -1523,7 +1633,11 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image,
h = MAX(1, h >> 1);
}
texture->total_data_size = tsize;
if (texture->target == GL_TEXTURE_CUBE_MAP || texture->target == GL_TEXTURE_2D_ARRAY) {
texture->total_data_size = tsize * texture->layers;
} else {
texture->total_data_size = tsize;
}
texture->stored_cube_sides |= (1 << p_layer);
@ -1548,7 +1662,7 @@ void TextureStorage::_texture_set_3d_data(RID p_texture, const Vector<Ref<Image>
Ref<Image> img = _get_gl_image_and_format(p_data[0], p_data[0]->get_format(), real_format, format, internal_format, type, compressed, texture->resize_to_po2);
ERR_FAIL_COND(img.is_null());
ERR_FAIL_COND_MSG(compressed, "Compressed 3D textures are not supported in the GL Compatibility backend.");
ERR_FAIL_COND_MSG(compressed, "Compressed 3D textures are not supported in the Compatibility renderer.");
glActiveTexture(GL_TEXTURE0);
glBindTexture(texture->target, texture->tex_id);
@ -1680,6 +1794,14 @@ uint32_t TextureStorage::texture_get_texid(RID p_texture) const {
return texture->tex_id;
}
Vector3i TextureStorage::texture_get_size(RID p_texture) const {
Texture *texture = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL_V(texture, Vector3i(0, 0, 0));
return Vector3i(texture->width, texture->height, texture->depth);
}
uint32_t TextureStorage::texture_get_width(RID p_texture) const {
Texture *texture = texture_owner.get_or_null(p_texture);
@ -2369,7 +2491,7 @@ Point2i TextureStorage::render_target_get_position(RID p_render_target) const {
ERR_FAIL_NULL_V(rt, Point2i());
return rt->position;
};
}
void TextureStorage::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
@ -2398,7 +2520,7 @@ Size2i TextureStorage::render_target_get_size(RID p_render_target) const {
return rt->size;
}
void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) {
void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
ERR_FAIL_COND(rt->direct_to_screen);
@ -2475,6 +2597,20 @@ RID TextureStorage::render_target_get_override_velocity(RID p_render_target) con
return rt->overridden.velocity;
}
void TextureStorage::render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
rt->render_region = p_render_region;
}
Rect2i TextureStorage::render_target_get_render_region(RID p_render_target) const {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL_V(rt, Rect2i());
return rt->render_region;
}
RID TextureStorage::render_target_get_texture(RID p_render_target) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL_V(rt, RID());

View file

@ -36,6 +36,7 @@
#include "platform_gl.h"
#include "config.h"
#include "core/io/image.h"
#include "core/os/os.h"
#include "core/templates/rid_owner.h"
#include "servers/rendering/renderer_compositor.h"
@ -126,6 +127,7 @@ enum DefaultGLTexture {
DEFAULT_GL_TEXTURE_3D_BLACK,
DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE,
DEFAULT_GL_TEXTURE_2D_UINT,
DEFAULT_GL_TEXTURE_EXT,
DEFAULT_GL_TEXTURE_MAX
};
@ -146,7 +148,7 @@ struct Texture {
RID self;
bool is_proxy = false;
bool is_external = false;
bool is_from_native_handle = false;
bool is_render_target = false;
RID proxy_to;
@ -209,7 +211,7 @@ struct Texture {
void copy_from(const Texture &o) {
proxy_to = o.proxy_to;
is_proxy = o.is_proxy;
is_external = o.is_external;
is_from_native_handle = o.is_from_native_handle;
width = o.width;
height = o.height;
alloc_width = o.alloc_width;
@ -370,6 +372,8 @@ struct RenderTarget {
RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
bool reattach_textures = false;
Rect2i render_region;
struct RTOverridden {
bool is_overridden = false;
RID color;
@ -476,8 +480,8 @@ public:
/* Canvas Texture API */
CanvasTexture *get_canvas_texture(RID p_rid) { return canvas_texture_owner.get_or_null(p_rid); };
bool owns_canvas_texture(RID p_rid) { return canvas_texture_owner.owns(p_rid); };
CanvasTexture *get_canvas_texture(RID p_rid) { return canvas_texture_owner.get_or_null(p_rid); }
bool owns_canvas_texture(RID p_rid) { return canvas_texture_owner.owns(p_rid); }
virtual RID canvas_texture_allocate() override;
virtual void canvas_texture_initialize(RID p_rid) override;
@ -497,29 +501,34 @@ public:
return texture_owner.get_or_null(texture->proxy_to);
}
return texture;
};
bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); };
}
bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); }
void texture_2d_initialize_from_texture(RID p_texture, Texture &p_tex) {
texture_owner.initialize_rid(p_texture, p_tex);
}
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_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent
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 RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;
virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override;
virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override;
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
Ref<Image> texture_2d_placeholder;
Vector<Ref<Image>> texture_2d_array_placeholder;
Vector<Ref<Image>> cubemap_placeholder;
Vector<Ref<Image>> texture_3d_placeholder;
//these two APIs can be used together or in combination with the others.
virtual void texture_2d_placeholder_initialize(RID p_texture) override;
virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override;
@ -553,6 +562,7 @@ public:
void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0);
virtual Image::Format texture_get_format(RID p_texture) const override;
uint32_t texture_get_texid(RID p_texture) const;
Vector3i texture_get_size(RID p_texture) const;
uint32_t texture_get_width(RID p_texture) const;
uint32_t texture_get_height(RID p_texture) const;
uint32_t texture_get_depth(RID p_texture) const;
@ -581,7 +591,7 @@ public:
virtual RID decal_allocate() override;
virtual void decal_initialize(RID p_rid) override;
virtual void decal_free(RID p_rid) override{};
virtual void decal_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;
@ -610,8 +620,8 @@ public:
static GLuint system_fbo;
RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); };
bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); };
RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }
bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); }
void check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture);
@ -677,13 +687,20 @@ public:
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 void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override;
virtual RID render_target_get_override_color(RID p_render_target) const override;
virtual RID render_target_get_override_depth(RID p_render_target) const override;
virtual RID render_target_get_override_velocity(RID p_render_target) const override;
virtual RID render_target_get_override_velocity_depth(RID p_render_target) const override { return RID(); }
virtual void render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) override;
virtual Rect2i render_target_get_render_region(RID p_render_target) const override;
virtual RID render_target_get_texture(RID p_render_target) override;
virtual void render_target_set_velocity_target_size(RID p_render_target, const Size2i &p_target_size) override {}
virtual Size2i render_target_get_velocity_target_size(RID p_render_target) const override { return Size2i(); }
void bind_framebuffer(GLuint framebuffer) {
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}

View file

@ -40,8 +40,6 @@
#include "particles_storage.h"
#include "texture_storage.h"
#include "servers/rendering/rendering_server_globals.h"
using namespace GLES3;
Utilities *Utilities::singleton = nullptr;
@ -467,4 +465,16 @@ Size2i Utilities::get_maximum_viewport_size() const {
return Size2i(config->max_viewport_size[0], config->max_viewport_size[1]);
}
uint32_t Utilities::get_maximum_shader_varyings() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, 31);
return config->max_shader_varyings;
}
uint64_t Utilities::get_maximum_uniform_buffer_size() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, 65536);
return uint64_t(config->max_uniform_buffer_size);
}
#endif // GLES3_ENABLED

View file

@ -165,8 +165,8 @@ public:
/* VISIBILITY NOTIFIER */
VisibilityNotifier *get_visibility_notifier(RID p_rid) { return visibility_notifier_owner.get_or_null(p_rid); };
bool owns_visibility_notifier(RID p_rid) const { return visibility_notifier_owner.owns(p_rid); };
VisibilityNotifier *get_visibility_notifier(RID p_rid) { return visibility_notifier_owner.get_or_null(p_rid); }
bool owns_visibility_notifier(RID p_rid) const { return visibility_notifier_owner.owns(p_rid); }
virtual RID visibility_notifier_allocate() override;
virtual void visibility_notifier_initialize(RID p_notifier) override;
@ -226,6 +226,8 @@ public:
virtual String get_video_adapter_api_version() const override;
virtual Size2i get_maximum_viewport_size() const override;
virtual uint32_t get_maximum_shader_varyings() const override;
virtual uint64_t get_maximum_uniform_buffer_size() const override;
};
} // namespace GLES3

View file

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

View file

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

View file

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

View file

@ -0,0 +1,156 @@
/**************************************************************************/
/* metal_device_properties.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
/**************************************************************************/
/* */
/* Portions of this code were derived from MoltenVK. */
/* */
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
/* (http://www.brenwill.com) */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); */
/* you may not use this file except in compliance with the License. */
/* You may obtain a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
/* implied. See the License for the specific language governing */
/* permissions and limitations under the License. */
/**************************************************************************/
#ifndef METAL_DEVICE_PROPERTIES_H
#define METAL_DEVICE_PROPERTIES_H
#import "servers/rendering/rendering_device.h"
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
/** The buffer index to use for vertex content. */
const static uint32_t VERT_CONTENT_BUFFER_INDEX = 0;
const static uint32_t MAX_COLOR_ATTACHMENT_COUNT = 8;
typedef NS_OPTIONS(NSUInteger, SampleCount) {
SampleCount1 = (1UL << 0),
SampleCount2 = (1UL << 1),
SampleCount4 = (1UL << 2),
SampleCount8 = (1UL << 3),
SampleCount16 = (1UL << 4),
SampleCount32 = (1UL << 5),
SampleCount64 = (1UL << 6),
};
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalFeatures {
uint32_t mslVersion = 0;
MTLGPUFamily highestFamily = MTLGPUFamilyApple4;
bool supportsBCTextureCompression = false;
bool supportsDepth24Stencil8 = false;
bool supports32BitFloatFiltering = false;
bool supports32BitMSAA = false;
bool supportsMac = TARGET_OS_OSX;
MTLLanguageVersion mslVersionEnum = MTLLanguageVersion1_2;
SampleCount supportedSampleCounts = SampleCount1;
long hostMemoryPageSize = 0;
bool layeredRendering = false;
bool multisampleLayeredRendering = false;
bool quadPermute = false; /**< If true, quadgroup permutation functions (vote, ballot, shuffle) are supported in shaders. */
bool simdPermute = false; /**< If true, SIMD-group permutation functions (vote, ballot, shuffle) are supported in shaders. */
bool simdReduction = false; /**< If true, SIMD-group reduction functions (arithmetic) are supported in shaders. */
bool tessellationShader = false; /**< If true, tessellation shaders are supported. */
bool imageCubeArray = false; /**< If true, image cube arrays are supported. */
MTLArgumentBuffersTier argument_buffers_tier = MTLArgumentBuffersTier1;
/// If true, argument encoders are required to encode arguments into an argument buffer.
bool needs_arg_encoders = true;
bool metal_fx_spatial = false; /**< If true, Metal FX spatial functions are supported. */
bool metal_fx_temporal = false; /**< If true, Metal FX temporal functions are supported. */
bool supports_gpu_address = false; /**< If true, referencing a GPU address in a shader is supported. */
};
struct MetalLimits {
uint64_t maxImageArrayLayers;
uint64_t maxFramebufferHeight;
uint64_t maxFramebufferWidth;
uint64_t maxImageDimension1D;
uint64_t maxImageDimension2D;
uint64_t maxImageDimension3D;
uint64_t maxImageDimensionCube;
uint64_t maxViewportDimensionX;
uint64_t maxViewportDimensionY;
MTLSize maxThreadsPerThreadGroup;
MTLSize maxComputeWorkGroupCount;
uint64_t maxBoundDescriptorSets;
uint64_t maxColorAttachments;
uint64_t maxTexturesPerArgumentBuffer;
uint64_t maxSamplersPerArgumentBuffer;
uint64_t maxBuffersPerArgumentBuffer;
uint64_t maxBufferLength;
uint64_t minUniformBufferOffsetAlignment;
uint64_t maxVertexDescriptorLayoutStride;
uint16_t maxViewports;
uint32_t maxPerStageBufferCount; /**< The total number of per-stage Metal buffers available for shader uniform content and attributes. */
uint32_t maxPerStageTextureCount; /**< The total number of per-stage Metal textures available for shader uniform content. */
uint32_t maxPerStageSamplerCount; /**< The total number of per-stage Metal samplers available for shader uniform content. */
uint32_t maxVertexInputAttributes;
uint32_t maxVertexInputBindings;
uint32_t maxVertexInputBindingStride;
uint32_t maxDrawIndexedIndexValue;
uint32_t maxShaderVaryings;
double temporalScalerInputContentMinScale;
double temporalScalerInputContentMaxScale;
uint32_t minSubgroupSize; /**< The minimum number of threads in a SIMD-group. */
uint32_t maxSubgroupSize; /**< The maximum number of threads in a SIMD-group. */
BitField<RDD::ShaderStage> subgroupSupportedShaderStages;
BitField<RD::SubgroupOperations> subgroupSupportedOperations; /**< The subgroup operations supported by the device. */
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalDeviceProperties {
private:
void init_features(id<MTLDevice> p_device);
void init_limits(id<MTLDevice> p_device);
public:
MetalFeatures features;
MetalLimits limits;
SampleCount find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const;
MetalDeviceProperties(id<MTLDevice> p_device);
~MetalDeviceProperties();
private:
static const SampleCount sample_count[RenderingDevice::TextureSamples::TEXTURE_SAMPLES_MAX];
};
#endif // METAL_DEVICE_PROPERTIES_H

View file

@ -0,0 +1,367 @@
/**************************************************************************/
/* metal_device_properties.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
/**************************************************************************/
/* */
/* Portions of this code were derived from MoltenVK. */
/* */
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
/* (http://www.brenwill.com) */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); */
/* you may not use this file except in compliance with the License. */
/* You may obtain a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
/* implied. See the License for the specific language governing */
/* permissions and limitations under the License. */
/**************************************************************************/
#import "metal_device_properties.h"
#import <Metal/Metal.h>
#import <MetalFX/MetalFX.h>
#import <spirv_cross.hpp>
#import <spirv_msl.hpp>
// Common scaling multipliers.
#define KIBI (1024)
#define MEBI (KIBI * KIBI)
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 140000) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < 170000)
#define MTLGPUFamilyApple9 (MTLGPUFamily)1009
#endif
API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
MTLGPUFamily &operator--(MTLGPUFamily &p_family) {
p_family = static_cast<MTLGPUFamily>(static_cast<int>(p_family) - 1);
if (p_family < MTLGPUFamilyApple1) {
p_family = MTLGPUFamilyApple9;
}
return p_family;
}
void MetalDeviceProperties::init_features(id<MTLDevice> p_device) {
features = {};
features.highestFamily = MTLGPUFamilyApple1;
for (MTLGPUFamily family = MTLGPUFamilyApple9; family >= MTLGPUFamilyApple1; --family) {
if ([p_device supportsFamily:family]) {
features.highestFamily = family;
break;
}
}
if (@available(macOS 11, iOS 16.4, tvOS 16.4, *)) {
features.supportsBCTextureCompression = p_device.supportsBCTextureCompression;
} else {
features.supportsBCTextureCompression = false;
}
#if TARGET_OS_OSX
features.supportsDepth24Stencil8 = p_device.isDepth24Stencil8PixelFormatSupported;
#endif
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
features.supports32BitFloatFiltering = p_device.supports32BitFloatFiltering;
features.supports32BitMSAA = p_device.supports32BitMSAA;
}
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
features.supports_gpu_address = true;
}
features.hostMemoryPageSize = sysconf(_SC_PAGESIZE);
for (SampleCount sc = SampleCount1; sc <= SampleCount64; sc <<= 1) {
if ([p_device supportsTextureSampleCount:sc]) {
features.supportedSampleCounts |= sc;
}
}
features.layeredRendering = [p_device supportsFamily:MTLGPUFamilyApple5];
features.multisampleLayeredRendering = [p_device supportsFamily:MTLGPUFamilyApple7];
features.tessellationShader = [p_device supportsFamily:MTLGPUFamilyApple3];
features.imageCubeArray = [p_device supportsFamily:MTLGPUFamilyApple3];
features.quadPermute = [p_device supportsFamily:MTLGPUFamilyApple4];
features.simdPermute = [p_device supportsFamily:MTLGPUFamilyApple6];
features.simdReduction = [p_device supportsFamily:MTLGPUFamilyApple7];
features.argument_buffers_tier = p_device.argumentBuffersSupport;
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
features.needs_arg_encoders = !([p_device supportsFamily:MTLGPUFamilyMetal3] && features.argument_buffers_tier == MTLArgumentBuffersTier2);
}
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
features.metal_fx_spatial = [MTLFXSpatialScalerDescriptor supportsDevice:p_device];
features.metal_fx_temporal = [MTLFXTemporalScalerDescriptor supportsDevice:p_device];
}
MTLCompileOptions *opts = [MTLCompileOptions new];
features.mslVersionEnum = opts.languageVersion; // By default, Metal uses the most recent language version.
#define setMSLVersion(m_maj, m_min) \
features.mslVersion = SPIRV_CROSS_NAMESPACE::CompilerMSL::Options::make_msl_version(m_maj, m_min)
switch (features.mslVersionEnum) {
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000 || __TV_OS_VERSION_MAX_ALLOWED >= 180000
case MTLLanguageVersion3_2:
setMSLVersion(3, 2);
break;
#endif
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 || __TV_OS_VERSION_MAX_ALLOWED >= 170000
case MTLLanguageVersion3_1:
setMSLVersion(3, 1);
break;
#endif
case MTLLanguageVersion3_0:
setMSLVersion(3, 0);
break;
case MTLLanguageVersion2_4:
setMSLVersion(2, 4);
break;
case MTLLanguageVersion2_3:
setMSLVersion(2, 3);
break;
case MTLLanguageVersion2_2:
setMSLVersion(2, 2);
break;
case MTLLanguageVersion2_1:
setMSLVersion(2, 1);
break;
case MTLLanguageVersion2_0:
setMSLVersion(2, 0);
break;
case MTLLanguageVersion1_2:
setMSLVersion(1, 2);
break;
case MTLLanguageVersion1_1:
setMSLVersion(1, 1);
break;
#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST
case MTLLanguageVersion1_0:
setMSLVersion(1, 0);
break;
#endif
}
}
void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
using std::max;
using std::min;
// FST: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
// FST: Maximum number of layers per 1D texture array, 2D texture array, or 3D texture.
limits.maxImageArrayLayers = 2048;
if ([p_device supportsFamily:MTLGPUFamilyApple3]) {
// FST: Maximum 2D texture width and height.
limits.maxFramebufferWidth = 16384;
limits.maxFramebufferHeight = 16384;
limits.maxViewportDimensionX = 16384;
limits.maxViewportDimensionY = 16384;
// FST: Maximum 1D texture width.
limits.maxImageDimension1D = 16384;
// FST: Maximum 2D texture width and height.
limits.maxImageDimension2D = 16384;
// FST: Maximum cube map texture width and height.
limits.maxImageDimensionCube = 16384;
} else {
// FST: Maximum 2D texture width and height.
limits.maxFramebufferWidth = 8192;
limits.maxFramebufferHeight = 8192;
limits.maxViewportDimensionX = 8192;
limits.maxViewportDimensionY = 8192;
// FST: Maximum 1D texture width.
limits.maxImageDimension1D = 8192;
// FST: Maximum 2D texture width and height.
limits.maxImageDimension2D = 8192;
// FST: Maximum cube map texture width and height.
limits.maxImageDimensionCube = 8192;
}
// FST: Maximum 3D texture width, height, and depth.
limits.maxImageDimension3D = 2048;
limits.maxThreadsPerThreadGroup = p_device.maxThreadsPerThreadgroup;
// No effective limits.
limits.maxComputeWorkGroupCount = { std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max() };
// https://github.com/KhronosGroup/MoltenVK/blob/568cc3acc0e2299931fdaecaaa1fc3ec5b4af281/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h#L85
limits.maxBoundDescriptorSets = SPIRV_CROSS_NAMESPACE::kMaxArgumentBuffers;
// FST: Maximum number of color render targets per render pass descriptor.
limits.maxColorAttachments = 8;
// Maximum number of textures the device can access, per stage, from an argument buffer.
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
limits.maxTexturesPerArgumentBuffer = 1'000'000;
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
limits.maxTexturesPerArgumentBuffer = 96;
} else {
limits.maxTexturesPerArgumentBuffer = 31;
}
// Maximum number of samplers the device can access, per stage, from an argument buffer.
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
limits.maxSamplersPerArgumentBuffer = 1024;
} else {
limits.maxSamplersPerArgumentBuffer = 16;
}
// Maximum number of buffers the device can access, per stage, from an argument buffer.
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
limits.maxBuffersPerArgumentBuffer = std::numeric_limits<uint64_t>::max();
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
limits.maxBuffersPerArgumentBuffer = 96;
} else {
limits.maxBuffersPerArgumentBuffer = 31;
}
limits.minSubgroupSize = limits.maxSubgroupSize = 1;
// These values were taken from MoltenVK.
if (features.simdPermute) {
limits.minSubgroupSize = 4;
limits.maxSubgroupSize = 32;
} else if (features.quadPermute) {
limits.minSubgroupSize = limits.maxSubgroupSize = 4;
}
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_COMPUTE_BIT);
if (features.tessellationShader) {
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT);
}
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_FRAGMENT_BIT);
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_BASIC_BIT);
if (features.simdPermute || features.quadPermute) {
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_VOTE_BIT);
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_BALLOT_BIT);
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_SHUFFLE_BIT);
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT);
}
if (features.simdReduction) {
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT);
}
if (features.quadPermute) {
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_QUAD_BIT);
}
limits.maxBufferLength = p_device.maxBufferLength;
// FST: Maximum size of vertex descriptor layout stride.
limits.maxVertexDescriptorLayoutStride = std::numeric_limits<uint64_t>::max();
// Maximum number of viewports.
if ([p_device supportsFamily:MTLGPUFamilyApple5]) {
limits.maxViewports = 16;
} else {
limits.maxViewports = 1;
}
limits.maxPerStageBufferCount = 31;
limits.maxPerStageSamplerCount = 16;
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
limits.maxPerStageTextureCount = 128;
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
limits.maxPerStageTextureCount = 96;
} else {
limits.maxPerStageTextureCount = 31;
}
limits.maxVertexInputAttributes = 31;
limits.maxVertexInputBindings = 31;
limits.maxVertexInputBindingStride = (2 * KIBI);
limits.maxShaderVaryings = 31; // Accurate on Apple4 and above. See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
limits.minUniformBufferOffsetAlignment = 64;
#endif
#if TARGET_OS_OSX
// This is Apple Silicon specific.
limits.minUniformBufferOffsetAlignment = 16;
#endif
limits.maxDrawIndexedIndexValue = std::numeric_limits<uint32_t>::max() - 1;
if (@available(macOS 14.0, iOS 17.0, tvOS 17.0, *)) {
limits.temporalScalerInputContentMinScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMinScaleForDevice:p_device];
limits.temporalScalerInputContentMaxScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMaxScaleForDevice:p_device];
} else {
// Defaults taken from macOS 14+
limits.temporalScalerInputContentMinScale = 1.0;
limits.temporalScalerInputContentMaxScale = 3.0;
}
}
MetalDeviceProperties::MetalDeviceProperties(id<MTLDevice> p_device) {
init_features(p_device);
init_limits(p_device);
}
MetalDeviceProperties::~MetalDeviceProperties() {
}
SampleCount MetalDeviceProperties::find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const {
SampleCount supported = features.supportedSampleCounts;
if (supported & sample_count[p_samples]) {
return sample_count[p_samples];
}
SampleCount requested_sample_count = sample_count[p_samples];
// Find the nearest supported sample count.
while (requested_sample_count > SampleCount1) {
if (supported & requested_sample_count) {
return requested_sample_count;
}
requested_sample_count = (SampleCount)(requested_sample_count >> 1);
}
return SampleCount1;
}
// region static members
const SampleCount MetalDeviceProperties::sample_count[RenderingDevice::TextureSamples::TEXTURE_SAMPLES_MAX] = {
SampleCount1,
SampleCount2,
SampleCount4,
SampleCount8,
SampleCount16,
SampleCount32,
SampleCount64,
};
// endregion

View file

@ -0,0 +1,979 @@
/**************************************************************************/
/* metal_objects.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
/**************************************************************************/
/* */
/* Portions of this code were derived from MoltenVK. */
/* */
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
/* (http://www.brenwill.com) */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); */
/* you may not use this file except in compliance with the License. */
/* You may obtain a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
/* implied. See the License for the specific language governing */
/* permissions and limitations under the License. */
/**************************************************************************/
#ifndef METAL_OBJECTS_H
#define METAL_OBJECTS_H
#import "metal_device_properties.h"
#import "metal_utils.h"
#import "pixel_formats.h"
#import "servers/rendering/rendering_device_driver.h"
#import <CommonCrypto/CommonDigest.h>
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#import <simd/simd.h>
#import <zlib.h>
#import <initializer_list>
#import <optional>
// These types can be used in Vector and other containers that use
// pointer operations not supported by ARC.
namespace MTL {
#define MTL_CLASS(name) \
class name { \
public: \
name(id<MTL##name> obj = nil) : m_obj(obj) {} \
operator id<MTL##name>() const { \
return m_obj; \
} \
id<MTL##name> m_obj; \
};
MTL_CLASS(Texture)
} //namespace MTL
/// Metal buffer index for the view mask when rendering multi-view.
const uint32_t VIEW_MASK_BUFFER_INDEX = 24;
enum ShaderStageUsage : uint32_t {
None = 0,
Vertex = RDD::SHADER_STAGE_VERTEX_BIT,
Fragment = RDD::SHADER_STAGE_FRAGMENT_BIT,
TesselationControl = RDD::SHADER_STAGE_TESSELATION_CONTROL_BIT,
TesselationEvaluation = RDD::SHADER_STAGE_TESSELATION_EVALUATION_BIT,
Compute = RDD::SHADER_STAGE_COMPUTE_BIT,
};
_FORCE_INLINE_ ShaderStageUsage &operator|=(ShaderStageUsage &p_a, int p_b) {
p_a = ShaderStageUsage(uint32_t(p_a) | uint32_t(p_b));
return p_a;
}
enum StageResourceUsage : uint32_t {
VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
};
typedef LocalVector<__unsafe_unretained id<MTLResource>> ResourceVector;
typedef HashMap<StageResourceUsage, ResourceVector> ResourceUsageMap;
enum class MDCommandBufferStateType {
None,
Render,
Compute,
Blit,
};
enum class MDPipelineType {
None,
Render,
Compute,
};
class MDRenderPass;
class MDPipeline;
class MDRenderPipeline;
class MDComputePipeline;
class MDFrameBuffer;
class RenderingDeviceDriverMetal;
class MDUniformSet;
class MDShader;
#pragma mark - Resource Factory
struct ClearAttKey {
const static uint32_t COLOR_COUNT = MAX_COLOR_ATTACHMENT_COUNT;
const static uint32_t DEPTH_INDEX = COLOR_COUNT;
const static uint32_t STENCIL_INDEX = DEPTH_INDEX + 1;
const static uint32_t ATTACHMENT_COUNT = STENCIL_INDEX + 1;
enum Flags : uint16_t {
CLEAR_FLAGS_NONE = 0,
CLEAR_FLAGS_LAYERED = 1 << 0,
};
Flags flags = CLEAR_FLAGS_NONE;
uint16_t sample_count = 0;
uint16_t pixel_formats[ATTACHMENT_COUNT] = { 0 };
_FORCE_INLINE_ void set_color_format(uint32_t p_idx, MTLPixelFormat p_fmt) { pixel_formats[p_idx] = p_fmt; }
_FORCE_INLINE_ void set_depth_format(MTLPixelFormat p_fmt) { pixel_formats[DEPTH_INDEX] = p_fmt; }
_FORCE_INLINE_ void set_stencil_format(MTLPixelFormat p_fmt) { pixel_formats[STENCIL_INDEX] = p_fmt; }
_FORCE_INLINE_ MTLPixelFormat depth_format() const { return (MTLPixelFormat)pixel_formats[DEPTH_INDEX]; }
_FORCE_INLINE_ MTLPixelFormat stencil_format() const { return (MTLPixelFormat)pixel_formats[STENCIL_INDEX]; }
_FORCE_INLINE_ void enable_layered_rendering() { flags::set(flags, CLEAR_FLAGS_LAYERED); }
_FORCE_INLINE_ bool is_enabled(uint32_t p_idx) const { return pixel_formats[p_idx] != 0; }
_FORCE_INLINE_ bool is_depth_enabled() const { return pixel_formats[DEPTH_INDEX] != 0; }
_FORCE_INLINE_ bool is_stencil_enabled() const { return pixel_formats[STENCIL_INDEX] != 0; }
_FORCE_INLINE_ bool is_layered_rendering_enabled() const { return flags::any(flags, CLEAR_FLAGS_LAYERED); }
_FORCE_INLINE_ bool operator==(const ClearAttKey &p_rhs) const {
return memcmp(this, &p_rhs, sizeof(ClearAttKey)) == 0;
}
uint32_t hash() const {
uint32_t h = hash_murmur3_one_32(flags);
h = hash_murmur3_one_32(sample_count, h);
h = hash_murmur3_buffer(pixel_formats, ATTACHMENT_COUNT * sizeof(pixel_formats[0]), h);
return hash_fmix32(h);
}
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDResourceFactory {
private:
RenderingDeviceDriverMetal *device_driver;
id<MTLFunction> new_func(NSString *p_source, NSString *p_name, NSError **p_error);
id<MTLFunction> new_clear_vert_func(ClearAttKey &p_key);
id<MTLFunction> new_clear_frag_func(ClearAttKey &p_key);
NSString *get_format_type_string(MTLPixelFormat p_fmt);
public:
id<MTLRenderPipelineState> new_clear_pipeline_state(ClearAttKey &p_key, NSError **p_error);
id<MTLDepthStencilState> new_depth_stencil_state(bool p_use_depth, bool p_use_stencil);
MDResourceFactory(RenderingDeviceDriverMetal *p_device_driver) :
device_driver(p_device_driver) {}
~MDResourceFactory() = default;
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDResourceCache {
private:
typedef HashMap<ClearAttKey, id<MTLRenderPipelineState>, HashableHasher<ClearAttKey>> HashMap;
std::unique_ptr<MDResourceFactory> resource_factory;
HashMap clear_states;
struct {
id<MTLDepthStencilState> all;
id<MTLDepthStencilState> depth_only;
id<MTLDepthStencilState> stencil_only;
id<MTLDepthStencilState> none;
} clear_depth_stencil_state;
public:
id<MTLRenderPipelineState> get_clear_render_pipeline_state(ClearAttKey &p_key, NSError **p_error);
id<MTLDepthStencilState> get_depth_stencil_state(bool p_use_depth, bool p_use_stencil);
explicit MDResourceCache(RenderingDeviceDriverMetal *p_device_driver) :
resource_factory(new MDResourceFactory(p_device_driver)) {}
~MDResourceCache() = default;
};
enum class MDAttachmentType : uint8_t {
None = 0,
Color = 1 << 0,
Depth = 1 << 1,
Stencil = 1 << 2,
};
_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) {
flags::set(p_a, p_b);
return p_a;
}
_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) {
return uint8_t(p_a) & uint8_t(p_b);
}
struct MDSubpass {
uint32_t subpass_index = 0;
uint32_t view_count = 0;
LocalVector<RDD::AttachmentReference> input_references;
LocalVector<RDD::AttachmentReference> color_references;
RDD::AttachmentReference depth_stencil_reference;
LocalVector<RDD::AttachmentReference> resolve_references;
MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const;
};
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDAttachment {
private:
uint32_t index = 0;
uint32_t firstUseSubpassIndex = 0;
uint32_t lastUseSubpassIndex = 0;
public:
MTLPixelFormat format = MTLPixelFormatInvalid;
MDAttachmentType type = MDAttachmentType::None;
MTLLoadAction loadAction = MTLLoadActionDontCare;
MTLStoreAction storeAction = MTLStoreActionDontCare;
MTLLoadAction stencilLoadAction = MTLLoadActionDontCare;
MTLStoreAction stencilStoreAction = MTLStoreActionDontCare;
uint32_t samples = 1;
/*!
* @brief Returns true if this attachment is first used in the given subpass.
* @param p_subpass
* @return
*/
_FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const {
return p_subpass.subpass_index == firstUseSubpassIndex;
}
/*!
* @brief Returns true if this attachment is last used in the given subpass.
* @param p_subpass
* @return
*/
_FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const {
return p_subpass.subpass_index == lastUseSubpassIndex;
}
void linkToSubpass(MDRenderPass const &p_pass);
MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass,
bool p_is_rendering_entire_area,
bool p_has_resolve,
bool p_can_resolve,
bool p_is_stencil) const;
bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc,
PixelFormats &p_pf,
MDSubpass const &p_subpass,
id<MTLTexture> p_attachment,
bool p_is_rendering_entire_area,
bool p_has_resolve,
bool p_can_resolve,
bool p_is_stencil) const;
/** Returns whether this attachment should be cleared in the subpass. */
bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const;
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDRenderPass {
public:
Vector<MDAttachment> attachments;
Vector<MDSubpass> subpasses;
uint32_t get_sample_count() const {
return attachments.is_empty() ? 1 : attachments[0].samples;
}
MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses);
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDCommandBuffer {
private:
RenderingDeviceDriverMetal *device_driver = nullptr;
id<MTLCommandQueue> queue = nil;
id<MTLCommandBuffer> commandBuffer = nil;
void _end_compute_dispatch();
void _end_blit();
#pragma mark - Render
void _render_set_dirty_state();
void _render_bind_uniform_sets();
void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects);
uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size);
void _end_render_pass();
void _render_clear_render_area();
public:
MDCommandBufferStateType type = MDCommandBufferStateType::None;
struct RenderState {
MDRenderPass *pass = nullptr;
MDFrameBuffer *frameBuffer = nullptr;
MDRenderPipeline *pipeline = nullptr;
LocalVector<RDD::RenderPassClearValue> clear_values;
LocalVector<MTLViewport> viewports;
LocalVector<MTLScissorRect> scissors;
std::optional<Color> blend_constants;
uint32_t current_subpass = UINT32_MAX;
Rect2i render_area = {};
bool is_rendering_entire_area = false;
MTLRenderPassDescriptor *desc = nil;
id<MTLRenderCommandEncoder> encoder = nil;
id<MTLBuffer> __unsafe_unretained index_buffer = nil; // Buffer is owned by RDD.
MTLIndexType index_type = MTLIndexTypeUInt16;
uint32_t index_offset = 0;
LocalVector<id<MTLBuffer> __unsafe_unretained> vertex_buffers;
LocalVector<NSUInteger> vertex_offsets;
ResourceUsageMap resource_usage;
// clang-format off
enum DirtyFlag: uint8_t {
DIRTY_NONE = 0b0000'0000,
DIRTY_PIPELINE = 0b0000'0001, //! pipeline state
DIRTY_UNIFORMS = 0b0000'0010, //! uniform sets
DIRTY_DEPTH = 0b0000'0100, //! depth / stenci state
DIRTY_VERTEX = 0b0000'1000, //! vertex buffers
DIRTY_VIEWPORT = 0b0001'0000, //! viewport rectangles
DIRTY_SCISSOR = 0b0010'0000, //! scissor rectangles
DIRTY_BLEND = 0b0100'0000, //! blend state
DIRTY_RASTER = 0b1000'0000, //! encoder state like cull mode
DIRTY_ALL = 0xff,
};
// clang-format on
BitField<DirtyFlag> dirty = DIRTY_NONE;
LocalVector<MDUniformSet *> uniform_sets;
// Bit mask of the uniform sets that are dirty, to prevent redundant binding.
uint64_t uniform_set_mask = 0;
_FORCE_INLINE_ void reset();
void end_encoding();
_ALWAYS_INLINE_ const MDSubpass &get_subpass() const {
DEV_ASSERT(pass != nullptr);
return pass->subpasses[current_subpass];
}
_FORCE_INLINE_ void mark_viewport_dirty() {
if (viewports.is_empty()) {
return;
}
dirty.set_flag(DirtyFlag::DIRTY_VIEWPORT);
}
_FORCE_INLINE_ void mark_scissors_dirty() {
if (scissors.is_empty()) {
return;
}
dirty.set_flag(DirtyFlag::DIRTY_SCISSOR);
}
_FORCE_INLINE_ void mark_vertex_dirty() {
if (vertex_buffers.is_empty()) {
return;
}
dirty.set_flag(DirtyFlag::DIRTY_VERTEX);
}
_FORCE_INLINE_ void mark_uniforms_dirty(std::initializer_list<uint32_t> l) {
if (uniform_sets.is_empty()) {
return;
}
for (uint32_t i : l) {
if (i < uniform_sets.size() && uniform_sets[i] != nullptr) {
uniform_set_mask |= 1 << i;
}
}
dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
}
_FORCE_INLINE_ void mark_uniforms_dirty(void) {
if (uniform_sets.is_empty()) {
return;
}
for (uint32_t i = 0; i < uniform_sets.size(); i++) {
if (uniform_sets[i] != nullptr) {
uniform_set_mask |= 1 << i;
}
}
dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
}
_FORCE_INLINE_ void mark_blend_dirty() {
if (!blend_constants.has_value()) {
return;
}
dirty.set_flag(DirtyFlag::DIRTY_BLEND);
}
MTLScissorRect clip_to_render_area(MTLScissorRect p_rect) const {
uint32_t raLeft = render_area.position.x;
uint32_t raRight = raLeft + render_area.size.width;
uint32_t raBottom = render_area.position.y;
uint32_t raTop = raBottom + render_area.size.height;
p_rect.x = CLAMP(p_rect.x, raLeft, MAX(raRight - 1, raLeft));
p_rect.y = CLAMP(p_rect.y, raBottom, MAX(raTop - 1, raBottom));
p_rect.width = MIN(p_rect.width, raRight - p_rect.x);
p_rect.height = MIN(p_rect.height, raTop - p_rect.y);
return p_rect;
}
Rect2i clip_to_render_area(Rect2i p_rect) const {
int32_t raLeft = render_area.position.x;
int32_t raRight = raLeft + render_area.size.width;
int32_t raBottom = render_area.position.y;
int32_t raTop = raBottom + render_area.size.height;
p_rect.position.x = CLAMP(p_rect.position.x, raLeft, MAX(raRight - 1, raLeft));
p_rect.position.y = CLAMP(p_rect.position.y, raBottom, MAX(raTop - 1, raBottom));
p_rect.size.width = MIN(p_rect.size.width, raRight - p_rect.position.x);
p_rect.size.height = MIN(p_rect.size.height, raTop - p_rect.position.y);
return p_rect;
}
} render;
// State specific for a compute pass.
struct ComputeState {
MDComputePipeline *pipeline = nullptr;
id<MTLComputeCommandEncoder> encoder = nil;
ResourceUsageMap resource_usage;
_FORCE_INLINE_ void reset() {
pipeline = nil;
encoder = nil;
// Keep the keys, as they are likely to be used again.
for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) {
kv.value.clear();
}
}
void end_encoding();
} compute;
// State specific to a blit pass.
struct {
id<MTLBlitCommandEncoder> encoder = nil;
_FORCE_INLINE_ void reset() {
encoder = nil;
}
} blit;
_FORCE_INLINE_ id<MTLCommandBuffer> get_command_buffer() const {
return commandBuffer;
}
void begin();
void commit();
void end();
id<MTLBlitCommandEncoder> blit_command_encoder();
void encodeRenderCommandEncoderWithDescriptor(MTLRenderPassDescriptor *p_desc, NSString *p_label);
void bind_pipeline(RDD::PipelineID p_pipeline);
#pragma mark - Render Commands
void render_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index);
void render_bind_uniform_sets(VectorView<RDD::UniformSetID> p_uniform_sets, RDD::ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count);
void render_clear_attachments(VectorView<RDD::AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects);
void render_set_viewport(VectorView<Rect2i> p_viewports);
void render_set_scissor(VectorView<Rect2i> p_scissors);
void render_set_blend_constants(const Color &p_constants);
void render_begin_pass(RDD::RenderPassID p_render_pass,
RDD::FramebufferID p_frameBuffer,
RDD::CommandBufferType p_cmd_buffer_type,
const Rect2i &p_rect,
VectorView<RDD::RenderPassClearValue> p_clear_values);
void render_next_subpass();
void render_draw(uint32_t p_vertex_count,
uint32_t p_instance_count,
uint32_t p_base_vertex,
uint32_t p_first_instance);
void render_bind_vertex_buffers(uint32_t p_binding_count, const RDD::BufferID *p_buffers, const uint64_t *p_offsets);
void render_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint64_t p_offset);
void render_draw_indexed(uint32_t p_index_count,
uint32_t p_instance_count,
uint32_t p_first_index,
int32_t p_vertex_offset,
uint32_t p_first_instance);
void render_draw_indexed_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride);
void render_draw_indexed_indirect_count(RDD::BufferID p_indirect_buffer, uint64_t p_offset, RDD::BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride);
void render_draw_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride);
void render_draw_indirect_count(RDD::BufferID p_indirect_buffer, uint64_t p_offset, RDD::BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride);
void render_end_pass();
#pragma mark - Compute Commands
void compute_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index);
void compute_bind_uniform_sets(VectorView<RDD::UniformSetID> p_uniform_sets, RDD::ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count);
void compute_dispatch(uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups);
void compute_dispatch_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset);
MDCommandBuffer(id<MTLCommandQueue> p_queue, RenderingDeviceDriverMetal *p_device_driver) :
device_driver(p_device_driver), queue(p_queue) {
type = MDCommandBufferStateType::None;
}
MDCommandBuffer() = default;
};
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 140000) || (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED < 170000)
#define MTLBindingAccess MTLArgumentAccess
#define MTLBindingAccessReadOnly MTLArgumentAccessReadOnly
#define MTLBindingAccessReadWrite MTLArgumentAccessReadWrite
#define MTLBindingAccessWriteOnly MTLArgumentAccessWriteOnly
#endif
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) BindingInfo {
MTLDataType dataType = MTLDataTypeNone;
uint32_t index = 0;
MTLBindingAccess access = MTLBindingAccessReadOnly;
MTLResourceUsage usage = 0;
MTLTextureType textureType = MTLTextureType2D;
int imageFormat = 0;
uint32_t arrayLength = 0;
bool isMultisampled = false;
inline MTLArgumentDescriptor *new_argument_descriptor() const {
MTLArgumentDescriptor *desc = MTLArgumentDescriptor.argumentDescriptor;
desc.dataType = dataType;
desc.index = index;
desc.access = access;
desc.textureType = textureType;
desc.arrayLength = arrayLength;
return desc;
}
size_t serialize_size() const {
return sizeof(uint32_t) * 8 /* 8 uint32_t fields */;
}
template <typename W>
void serialize(W &p_writer) const {
p_writer.write((uint32_t)dataType);
p_writer.write(index);
p_writer.write((uint32_t)access);
p_writer.write((uint32_t)usage);
p_writer.write((uint32_t)textureType);
p_writer.write(imageFormat);
p_writer.write(arrayLength);
p_writer.write(isMultisampled);
}
template <typename R>
void deserialize(R &p_reader) {
p_reader.read((uint32_t &)dataType);
p_reader.read(index);
p_reader.read((uint32_t &)access);
p_reader.read((uint32_t &)usage);
p_reader.read((uint32_t &)textureType);
p_reader.read((uint32_t &)imageFormat);
p_reader.read(arrayLength);
p_reader.read(isMultisampled);
}
};
using RDC = RenderingDeviceCommons;
typedef API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) HashMap<RDC::ShaderStage, BindingInfo> BindingInfoMap;
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) UniformInfo {
uint32_t binding;
ShaderStageUsage active_stages = None;
BindingInfoMap bindings;
BindingInfoMap bindings_secondary;
};
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) UniformSet {
LocalVector<UniformInfo> uniforms;
uint32_t buffer_size = 0;
HashMap<RDC::ShaderStage, uint32_t> offsets;
HashMap<RDC::ShaderStage, id<MTLArgumentEncoder>> encoders;
};
struct ShaderCacheEntry;
enum class ShaderLoadStrategy {
DEFAULT,
LAZY,
};
/// A Metal shader library.
@interface MDLibrary : NSObject {
ShaderCacheEntry *_entry;
};
- (id<MTLLibrary>)library;
- (NSError *)error;
- (void)setLabel:(NSString *)label;
+ (instancetype)newLibraryWithCacheEntry:(ShaderCacheEntry *)entry
device:(id<MTLDevice>)device
source:(NSString *)source
options:(MTLCompileOptions *)options
strategy:(ShaderLoadStrategy)strategy;
@end
struct SHA256Digest {
unsigned char data[CC_SHA256_DIGEST_LENGTH];
uint32_t hash() const {
uint32_t c = crc32(0, data, CC_SHA256_DIGEST_LENGTH);
return c;
}
SHA256Digest() {
bzero(data, CC_SHA256_DIGEST_LENGTH);
}
SHA256Digest(const char *p_data, size_t p_length) {
CC_SHA256(p_data, (CC_LONG)p_length, data);
}
_FORCE_INLINE_ uint32_t short_sha() const {
return __builtin_bswap32(*(uint32_t *)&data[0]);
}
};
template <>
struct HashMapComparatorDefault<SHA256Digest> {
static bool compare(const SHA256Digest &p_lhs, const SHA256Digest &p_rhs) {
return memcmp(p_lhs.data, p_rhs.data, CC_SHA256_DIGEST_LENGTH) == 0;
}
};
/// A cache entry for a Metal shader library.
struct ShaderCacheEntry {
RenderingDeviceDriverMetal &owner;
/// A hash of the Metal shader source code.
SHA256Digest key;
CharString name;
RD::ShaderStage stage = RD::SHADER_STAGE_VERTEX;
/// This reference must be weak, to ensure that when the last strong reference to the library
/// is released, the cache entry is freed.
MDLibrary *__weak library = nil;
/// Notify the cache that this entry is no longer needed.
void notify_free() const;
ShaderCacheEntry(RenderingDeviceDriverMetal &p_owner, SHA256Digest p_key) :
owner(p_owner), key(p_key) {
}
~ShaderCacheEntry() = default;
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDShader {
public:
CharString name;
Vector<UniformSet> sets;
bool uses_argument_buffers = true;
virtual void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) = 0;
MDShader(CharString p_name, Vector<UniformSet> p_sets, bool p_uses_argument_buffers) :
name(p_name), sets(p_sets), uses_argument_buffers(p_uses_argument_buffers) {}
virtual ~MDShader() = default;
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDComputeShader final : public MDShader {
public:
struct {
uint32_t binding = -1;
uint32_t size = 0;
} push_constants;
MTLSize local = {};
MDLibrary *kernel;
#if DEV_ENABLED
CharString kernel_source;
#endif
void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
MDComputeShader(CharString p_name, Vector<UniformSet> p_sets, bool p_uses_argument_buffers, MDLibrary *p_kernel);
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDRenderShader final : public MDShader {
public:
struct {
struct {
int32_t binding = -1;
uint32_t size = 0;
} vert;
struct {
int32_t binding = -1;
uint32_t size = 0;
} frag;
} push_constants;
bool needs_view_mask_buffer = false;
MDLibrary *vert;
MDLibrary *frag;
#if DEV_ENABLED
CharString vert_source;
CharString frag_source;
#endif
void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
MDRenderShader(CharString p_name,
Vector<UniformSet> p_sets,
bool p_needs_view_mask_buffer,
bool p_uses_argument_buffers,
MDLibrary *p_vert, MDLibrary *p_frag);
};
_FORCE_INLINE_ StageResourceUsage &operator|=(StageResourceUsage &p_a, uint32_t p_b) {
p_a = StageResourceUsage(uint32_t(p_a) | p_b);
return p_a;
}
_FORCE_INLINE_ StageResourceUsage stage_resource_usage(RDC::ShaderStage p_stage, MTLResourceUsage p_usage) {
return StageResourceUsage(p_usage << (p_stage * 2));
}
_FORCE_INLINE_ MTLResourceUsage resource_usage_for_stage(StageResourceUsage p_usage, RDC::ShaderStage p_stage) {
return MTLResourceUsage((p_usage >> (p_stage * 2)) & 0b11);
}
template <>
struct HashMapComparatorDefault<RDD::ShaderID> {
static bool compare(const RDD::ShaderID &p_lhs, const RDD::ShaderID &p_rhs) {
return p_lhs.id == p_rhs.id;
}
};
struct BoundUniformSet {
id<MTLBuffer> buffer;
ResourceUsageMap usage_to_resources;
/// Perform a 2-way merge each key of `ResourceVector` resources from this set into the
/// destination set.
///
/// Assumes the vectors of resources are sorted.
void merge_into(ResourceUsageMap &p_dst) const;
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDUniformSet {
private:
void bind_uniforms_argument_buffers(MDShader *p_shader, MDCommandBuffer::RenderState &p_state);
void bind_uniforms_direct(MDShader *p_shader, MDCommandBuffer::RenderState &p_state);
void bind_uniforms_argument_buffers(MDShader *p_shader, MDCommandBuffer::ComputeState &p_state);
void bind_uniforms_direct(MDShader *p_shader, MDCommandBuffer::ComputeState &p_state);
public:
uint32_t index;
LocalVector<RDD::BoundUniform> uniforms;
HashMap<MDShader *, BoundUniformSet> bound_uniforms;
void bind_uniforms(MDShader *p_shader, MDCommandBuffer::RenderState &p_state);
void bind_uniforms(MDShader *p_shader, MDCommandBuffer::ComputeState &p_state);
BoundUniformSet &bound_uniform_set(MDShader *p_shader, id<MTLDevice> p_device, ResourceUsageMap &p_resource_usage);
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDPipeline {
public:
MDPipelineType type;
explicit MDPipeline(MDPipelineType p_type) :
type(p_type) {}
virtual ~MDPipeline() = default;
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDRenderPipeline final : public MDPipeline {
public:
id<MTLRenderPipelineState> state = nil;
id<MTLDepthStencilState> depth_stencil = nil;
uint32_t push_constant_size = 0;
uint32_t push_constant_stages_mask = 0;
SampleCount sample_count = SampleCount1;
struct {
MTLCullMode cull_mode = MTLCullModeNone;
MTLTriangleFillMode fill_mode = MTLTriangleFillModeFill;
MTLDepthClipMode clip_mode = MTLDepthClipModeClip;
MTLWinding winding = MTLWindingClockwise;
MTLPrimitiveType render_primitive = MTLPrimitiveTypePoint;
struct {
bool enabled = false;
} depth_test;
struct {
bool enabled = false;
float depth_bias = 0.0;
float slope_scale = 0.0;
float clamp = 0.0;
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
if (!enabled) {
return;
}
[p_enc setDepthBias:depth_bias slopeScale:slope_scale clamp:clamp];
}
} depth_bias;
struct {
bool enabled = false;
uint32_t front_reference = 0;
uint32_t back_reference = 0;
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
if (!enabled) {
return;
}
[p_enc setStencilFrontReferenceValue:front_reference backReferenceValue:back_reference];
}
} stencil;
struct {
bool enabled = false;
float r = 0.0;
float g = 0.0;
float b = 0.0;
float a = 0.0;
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
//if (!enabled)
// return;
[p_enc setBlendColorRed:r green:g blue:b alpha:a];
}
} blend;
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
[p_enc setCullMode:cull_mode];
[p_enc setTriangleFillMode:fill_mode];
[p_enc setDepthClipMode:clip_mode];
[p_enc setFrontFacingWinding:winding];
depth_bias.apply(p_enc);
stencil.apply(p_enc);
blend.apply(p_enc);
}
} raster_state;
MDRenderShader *shader = nil;
MDRenderPipeline() :
MDPipeline(MDPipelineType::Render) {}
~MDRenderPipeline() final = default;
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDComputePipeline final : public MDPipeline {
public:
id<MTLComputePipelineState> state = nil;
struct {
MTLSize local = {};
} compute_state;
MDComputeShader *shader = nil;
explicit MDComputePipeline(id<MTLComputePipelineState> p_state) :
MDPipeline(MDPipelineType::Compute), state(p_state) {}
~MDComputePipeline() final = default;
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDFrameBuffer {
Vector<MTL::Texture> textures;
public:
Size2i size;
MDFrameBuffer(Vector<MTL::Texture> p_textures, Size2i p_size) :
textures(p_textures), size(p_size) {}
MDFrameBuffer() {}
/// Returns the texture at the given index.
_ALWAYS_INLINE_ MTL::Texture get_texture(uint32_t p_idx) const {
return textures[p_idx];
}
/// Returns true if the texture at the given index is not nil.
_ALWAYS_INLINE_ bool has_texture(uint32_t p_idx) const {
return textures[p_idx] != nil;
}
/// Set the texture at the given index.
_ALWAYS_INLINE_ void set_texture(uint32_t p_idx, MTL::Texture p_texture) {
textures.write[p_idx] = p_texture;
}
/// Unset or nil the texture at the given index.
_ALWAYS_INLINE_ void unset_texture(uint32_t p_idx) {
textures.write[p_idx] = nil;
}
/// Resizes buffers to the specified size.
_ALWAYS_INLINE_ void set_texture_count(uint32_t p_size) {
textures.resize(p_size);
}
virtual ~MDFrameBuffer() = default;
};
// These functions are used to convert between Objective-C objects and
// the RIDs used by Godot, respecting automatic reference counting.
namespace rid {
// Converts an Objective-C object to a pointer, and incrementing the
// reference count.
_FORCE_INLINE_
void *owned(id p_id) {
return (__bridge_retained void *)p_id;
}
#define MAKE_ID(FROM, TO) \
_FORCE_INLINE_ TO make(FROM p_obj) { \
return TO(owned(p_obj)); \
}
MAKE_ID(id<MTLTexture>, RDD::TextureID)
MAKE_ID(id<MTLBuffer>, RDD::BufferID)
MAKE_ID(id<MTLSamplerState>, RDD::SamplerID)
MAKE_ID(MTLVertexDescriptor *, RDD::VertexFormatID)
MAKE_ID(id<MTLCommandQueue>, RDD::CommandPoolID)
// Converts a pointer to an Objective-C object without changing the reference count.
_FORCE_INLINE_
auto get(RDD::ID p_id) {
return (p_id.id) ? (__bridge ::id)(void *)p_id.id : nil;
}
// Converts a pointer to an Objective-C object, and decrements the reference count.
_FORCE_INLINE_
auto release(RDD::ID p_id) {
return (__bridge_transfer ::id)(void *)p_id.id;
}
} // namespace rid
#endif // METAL_OBJECTS_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,105 @@
/**************************************************************************/
/* metal_utils.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 METAL_UTILS_H
#define METAL_UTILS_H
#import <os/log.h>
#pragma mark - Boolean flags
namespace flags {
/*! Sets the flags within the value parameter specified by the mask parameter. */
template <typename Tv, typename Tm>
void set(Tv &p_value, Tm p_mask) {
using T = std::underlying_type_t<Tv>;
p_value = static_cast<Tv>(static_cast<T>(p_value) | static_cast<T>(p_mask));
}
/*! Clears the flags within the value parameter specified by the mask parameter. */
template <typename Tv, typename Tm>
void clear(Tv &p_value, Tm p_mask) {
using T = std::underlying_type_t<Tv>;
p_value = static_cast<Tv>(static_cast<T>(p_value) & ~static_cast<T>(p_mask));
}
/*! Returns whether the specified value has any of the bits specified in mask set to 1. */
template <typename Tv, typename Tm>
static constexpr bool any(Tv p_value, const Tm p_mask) {
return ((p_value & p_mask) != 0);
}
/*! Returns whether the specified value has all of the bits specified in mask set to 1. */
template <typename Tv, typename Tm>
static constexpr bool all(Tv p_value, const Tm p_mask) {
return ((p_value & p_mask) == p_mask);
}
} //namespace flags
#pragma mark - Alignment and Offsets
static constexpr bool is_power_of_two(uint64_t p_value) {
return p_value && ((p_value & (p_value - 1)) == 0);
}
static constexpr uint64_t round_up_to_alignment(uint64_t p_value, uint64_t p_alignment) {
DEV_ASSERT(is_power_of_two(p_alignment));
if (p_alignment == 0) {
return p_value;
}
uint64_t mask = p_alignment - 1;
uint64_t aligned_value = (p_value + mask) & ~mask;
return aligned_value;
}
class Defer {
public:
Defer(std::function<void()> func) :
func_(func) {}
~Defer() { func_(); }
private:
std::function<void()> func_;
};
#define CONCAT_INTERNAL(x, y) x##y
#define CONCAT(x, y) CONCAT_INTERNAL(x, y)
#define DEFER const Defer &CONCAT(defer__, __LINE__) = Defer
extern os_log_t LOG_DRIVER;
// Used for dynamic tracing.
extern os_log_t LOG_INTERVALS;
#endif // METAL_UTILS_H

View file

@ -0,0 +1,413 @@
/**************************************************************************/
/* pixel_formats.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
/**************************************************************************/
/* */
/* Portions of this code were derived from MoltenVK. */
/* */
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
/* (http://www.brenwill.com) */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); */
/* you may not use this file except in compliance with the License. */
/* You may obtain a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
/* implied. See the License for the specific language governing */
/* permissions and limitations under the License. */
/**************************************************************************/
#ifndef PIXEL_FORMATS_H
#define PIXEL_FORMATS_H
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#import "inflection_map.h"
#import "metal_device_properties.h"
#import "servers/rendering/rendering_device.h"
#import <Metal/Metal.h>
#pragma mark -
#pragma mark Metal format capabilities
typedef enum : uint16_t {
kMTLFmtCapsNone = 0,
/*! The format can be used in a shader read operation. */
kMTLFmtCapsRead = (1 << 0),
/*! The format can be used in a shader filter operation during sampling. */
kMTLFmtCapsFilter = (1 << 1),
/*! The format can be used in a shader write operation. */
kMTLFmtCapsWrite = (1 << 2),
/*! The format can be used with atomic operations. */
kMTLFmtCapsAtomic = (1 << 3),
/*! The format can be used as a color attachment. */
kMTLFmtCapsColorAtt = (1 << 4),
/*! The format can be used as a depth-stencil attachment. */
kMTLFmtCapsDSAtt = (1 << 5),
/*! The format can be used with blend operations. */
kMTLFmtCapsBlend = (1 << 6),
/*! The format can be used as a destination for multisample antialias (MSAA) data. */
kMTLFmtCapsMSAA = (1 << 7),
/*! The format can be used as a resolve attachment. */
kMTLFmtCapsResolve = (1 << 8),
kMTLFmtCapsVertex = (1 << 9),
kMTLFmtCapsRF = (kMTLFmtCapsRead | kMTLFmtCapsFilter),
kMTLFmtCapsRC = (kMTLFmtCapsRead | kMTLFmtCapsColorAtt),
kMTLFmtCapsRCB = (kMTLFmtCapsRC | kMTLFmtCapsBlend),
kMTLFmtCapsRCM = (kMTLFmtCapsRC | kMTLFmtCapsMSAA),
kMTLFmtCapsRCMB = (kMTLFmtCapsRCM | kMTLFmtCapsBlend),
kMTLFmtCapsRWC = (kMTLFmtCapsRC | kMTLFmtCapsWrite),
kMTLFmtCapsRWCB = (kMTLFmtCapsRWC | kMTLFmtCapsBlend),
kMTLFmtCapsRWCM = (kMTLFmtCapsRWC | kMTLFmtCapsMSAA),
kMTLFmtCapsRWCMB = (kMTLFmtCapsRWCM | kMTLFmtCapsBlend),
kMTLFmtCapsRFCMRB = (kMTLFmtCapsRCMB | kMTLFmtCapsFilter | kMTLFmtCapsResolve),
kMTLFmtCapsRFWCMB = (kMTLFmtCapsRWCMB | kMTLFmtCapsFilter),
kMTLFmtCapsAll = (kMTLFmtCapsRFWCMB | kMTLFmtCapsResolve),
kMTLFmtCapsDRM = (kMTLFmtCapsDSAtt | kMTLFmtCapsRead | kMTLFmtCapsMSAA),
kMTLFmtCapsDRFM = (kMTLFmtCapsDRM | kMTLFmtCapsFilter),
kMTLFmtCapsDRMR = (kMTLFmtCapsDRM | kMTLFmtCapsResolve),
kMTLFmtCapsDRFMR = (kMTLFmtCapsDRFM | kMTLFmtCapsResolve),
kMTLFmtCapsChromaSubsampling = kMTLFmtCapsRF,
kMTLFmtCapsMultiPlanar = kMTLFmtCapsChromaSubsampling,
} MTLFmtCaps;
inline MTLFmtCaps operator|(MTLFmtCaps p_left, MTLFmtCaps p_right) {
return static_cast<MTLFmtCaps>(static_cast<uint32_t>(p_left) | p_right);
}
inline MTLFmtCaps &operator|=(MTLFmtCaps &p_left, MTLFmtCaps p_right) {
return (p_left = p_left | p_right);
}
#pragma mark -
#pragma mark Metal view classes
enum class MTLViewClass : uint8_t {
None,
Color8,
Color16,
Color32,
Color64,
Color128,
PVRTC_RGB_2BPP,
PVRTC_RGB_4BPP,
PVRTC_RGBA_2BPP,
PVRTC_RGBA_4BPP,
EAC_R11,
EAC_RG11,
EAC_RGBA8,
ETC2_RGB8,
ETC2_RGB8A1,
ASTC_4x4,
ASTC_5x4,
ASTC_5x5,
ASTC_6x5,
ASTC_6x6,
ASTC_8x5,
ASTC_8x6,
ASTC_8x8,
ASTC_10x5,
ASTC_10x6,
ASTC_10x8,
ASTC_10x10,
ASTC_12x10,
ASTC_12x12,
BC1_RGBA,
BC2_RGBA,
BC3_RGBA,
BC4_R,
BC5_RG,
BC6H_RGB,
BC7_RGBA,
Depth24_Stencil8,
Depth32_Stencil8,
BGRA10_XR,
BGR10_XR
};
#pragma mark -
#pragma mark Format descriptors
/** Enumerates the data type of a format. */
enum class MTLFormatType {
None, /**< Format type is unknown. */
ColorHalf, /**< A 16-bit floating point color. */
ColorFloat, /**< A 32-bit floating point color. */
ColorInt8, /**< A signed 8-bit integer color. */
ColorUInt8, /**< An unsigned 8-bit integer color. */
ColorInt16, /**< A signed 16-bit integer color. */
ColorUInt16, /**< An unsigned 16-bit integer color. */
ColorInt32, /**< A signed 32-bit integer color. */
ColorUInt32, /**< An unsigned 32-bit integer color. */
DepthStencil, /**< A depth and stencil value. */
Compressed, /**< A block-compressed color. */
};
struct Extent2D {
uint32_t width;
uint32_t height;
};
struct ComponentMapping {
RD::TextureSwizzle r = RD::TEXTURE_SWIZZLE_IDENTITY;
RD::TextureSwizzle g = RD::TEXTURE_SWIZZLE_IDENTITY;
RD::TextureSwizzle b = RD::TEXTURE_SWIZZLE_IDENTITY;
RD::TextureSwizzle a = RD::TEXTURE_SWIZZLE_IDENTITY;
};
/** Describes the properties of a DataFormat, including the corresponding Metal pixel and vertex format. */
struct DataFormatDesc {
RD::DataFormat dataFormat;
MTLPixelFormat mtlPixelFormat;
MTLPixelFormat mtlPixelFormatSubstitute;
MTLVertexFormat mtlVertexFormat;
MTLVertexFormat mtlVertexFormatSubstitute;
uint8_t chromaSubsamplingPlaneCount;
uint8_t chromaSubsamplingComponentBits;
Extent2D blockTexelSize;
uint32_t bytesPerBlock;
MTLFormatType formatType;
ComponentMapping componentMapping;
const char *name;
bool hasReportedSubstitution;
inline double bytesPerTexel() const { return (double)bytesPerBlock / (double)(blockTexelSize.width * blockTexelSize.height); }
inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid || chromaSubsamplingPlaneCount > 1); }
inline bool isSupportedOrSubstitutable() const { return isSupported() || (mtlPixelFormatSubstitute != MTLPixelFormatInvalid); }
inline bool vertexIsSupported() const { return (mtlVertexFormat != MTLVertexFormatInvalid); }
inline bool vertexIsSupportedOrSubstitutable() const { return vertexIsSupported() || (mtlVertexFormatSubstitute != MTLVertexFormatInvalid); }
bool needsSwizzle() const {
return (componentMapping.r != RD::TEXTURE_SWIZZLE_IDENTITY ||
componentMapping.g != RD::TEXTURE_SWIZZLE_IDENTITY ||
componentMapping.b != RD::TEXTURE_SWIZZLE_IDENTITY ||
componentMapping.a != RD::TEXTURE_SWIZZLE_IDENTITY);
}
};
/** Describes the properties of a MTLPixelFormat or MTLVertexFormat. */
struct MTLFormatDesc {
union {
MTLPixelFormat mtlPixelFormat;
MTLVertexFormat mtlVertexFormat;
};
RD::DataFormat dataFormat = RD::DATA_FORMAT_MAX;
MTLFmtCaps mtlFmtCaps;
MTLViewClass mtlViewClass;
MTLPixelFormat mtlPixelFormatLinear;
const char *name = nullptr;
inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid) && (mtlFmtCaps != kMTLFmtCapsNone); }
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) PixelFormats {
using DataFormat = RD::DataFormat;
public:
/** Returns whether the DataFormat is supported by the GPU bound to this instance. */
bool isSupported(DataFormat p_format);
/** Returns whether the DataFormat is supported by this implementation, or can be substituted by one that is. */
bool isSupportedOrSubstitutable(DataFormat p_format);
/** Returns whether the specified Metal MTLPixelFormat can be used as a depth format. */
_FORCE_INLINE_ bool isDepthFormat(MTLPixelFormat p_format) {
switch (p_format) {
case MTLPixelFormatDepth32Float:
case MTLPixelFormatDepth16Unorm:
case MTLPixelFormatDepth32Float_Stencil8:
#if TARGET_OS_OSX
case MTLPixelFormatDepth24Unorm_Stencil8:
#endif
return true;
default:
return false;
}
}
/** Returns whether the specified Metal MTLPixelFormat can be used as a stencil format. */
_FORCE_INLINE_ bool isStencilFormat(MTLPixelFormat p_format) {
switch (p_format) {
case MTLPixelFormatStencil8:
#if TARGET_OS_OSX
case MTLPixelFormatDepth24Unorm_Stencil8:
case MTLPixelFormatX24_Stencil8:
#endif
case MTLPixelFormatDepth32Float_Stencil8:
case MTLPixelFormatX32_Stencil8:
return true;
default:
return false;
}
}
/** Returns whether the specified Metal MTLPixelFormat is a PVRTC format. */
bool isPVRTCFormat(MTLPixelFormat p_format);
/** Returns the format type corresponding to the specified Godot pixel format, */
MTLFormatType getFormatType(DataFormat p_format);
/** Returns the format type corresponding to the specified Metal MTLPixelFormat, */
MTLFormatType getFormatType(MTLPixelFormat p_format);
/**
* Returns the Metal MTLPixelFormat corresponding to the specified Godot pixel
* or returns MTLPixelFormatInvalid if no corresponding MTLPixelFormat exists.
*/
MTLPixelFormat getMTLPixelFormat(DataFormat p_format);
/**
* Returns the DataFormat corresponding to the specified Metal MTLPixelFormat,
* or returns DATA_FORMAT_MAX if no corresponding DataFormat exists.
*/
DataFormat getDataFormat(MTLPixelFormat p_format);
/**
* Returns the size, in bytes, of a texel block of the specified Godot pixel.
* For uncompressed formats, the returned value corresponds to the size in bytes of a single texel.
*/
uint32_t getBytesPerBlock(DataFormat p_format);
/**
* Returns the size, in bytes, of a texel block of the specified Metal format.
* For uncompressed formats, the returned value corresponds to the size in bytes of a single texel.
*/
uint32_t getBytesPerBlock(MTLPixelFormat p_format);
/** Returns the number of planes of the specified chroma-subsampling (YCbCr) DataFormat */
uint8_t getChromaSubsamplingPlaneCount(DataFormat p_format);
/** Returns the number of bits per channel of the specified chroma-subsampling (YCbCr) DataFormat */
uint8_t getChromaSubsamplingComponentBits(DataFormat p_format);
/**
* Returns the size, in bytes, of a texel of the specified Godot format.
* The returned value may be fractional for certain compressed formats.
*/
float getBytesPerTexel(DataFormat p_format);
/**
* Returns the size, in bytes, of a texel of the specified Metal format.
* The returned value may be fractional for certain compressed formats.
*/
float getBytesPerTexel(MTLPixelFormat p_format);
/**
* Returns the size, in bytes, of a row of texels of the specified Godot pixel format.
*
* For compressed formats, this takes into consideration the compression block size,
* and p_texels_per_row should specify the width in texels, not blocks. The result is rounded
* up if p_texels_per_row is not an integer multiple of the compression block width.
*/
size_t getBytesPerRow(DataFormat p_format, uint32_t p_texels_per_row);
/**
* Returns the size, in bytes, of a row of texels of the specified Metal format.
*
* For compressed formats, this takes into consideration the compression block size,
* and texelsPerRow should specify the width in texels, not blocks. The result is rounded
* up if texelsPerRow is not an integer multiple of the compression block width.
*/
size_t getBytesPerRow(MTLPixelFormat p_format, uint32_t p_texels_per_row);
/**
* Returns the size, in bytes, of a texture layer of the specified Godot pixel format.
*
* For compressed formats, this takes into consideration the compression block size,
* and p_texel_rows_per_layer should specify the height in texels, not blocks. The result is
* rounded up if p_texel_rows_per_layer is not an integer multiple of the compression block height.
*/
size_t getBytesPerLayer(DataFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer);
/**
* Returns the size, in bytes, of a texture layer of the specified Metal format.
* For compressed formats, this takes into consideration the compression block size,
* and p_texel_rows_per_layer should specify the height in texels, not blocks. The result is
* rounded up if p_texel_rows_per_layer is not an integer multiple of the compression block height.
*/
size_t getBytesPerLayer(MTLPixelFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer);
/** Returns whether or not the specified Godot format requires swizzling to use with Metal. */
bool needsSwizzle(DataFormat p_format);
/** Returns the Metal format capabilities supported by the specified Godot format, without substitution. */
MTLFmtCaps getCapabilities(DataFormat p_format, bool p_extended = false);
/** Returns the Metal format capabilities supported by the specified Metal format. */
MTLFmtCaps getCapabilities(MTLPixelFormat p_format, bool p_extended = false);
/**
* Returns the Metal MTLVertexFormat corresponding to the specified
* DataFormat as used as a vertex attribute format.
*/
MTLVertexFormat getMTLVertexFormat(DataFormat p_format);
#pragma mark Construction
explicit PixelFormats(id<MTLDevice> p_device, const MetalFeatures &p_feat);
protected:
DataFormatDesc &getDataFormatDesc(DataFormat p_format);
DataFormatDesc &getDataFormatDesc(MTLPixelFormat p_format);
MTLFormatDesc &getMTLPixelFormatDesc(MTLPixelFormat p_format);
MTLFmtCaps &getMTLPixelFormatCapsIf(MTLPixelFormat mtlPixFmt, bool cond);
MTLFormatDesc &getMTLVertexFormatDesc(MTLVertexFormat p_format);
void initDataFormatCapabilities();
void initMTLPixelFormatCapabilities();
void initMTLVertexFormatCapabilities(const MetalFeatures &p_feat);
void modifyMTLFormatCapabilities(const MetalFeatures &p_feat);
void buildDFFormatMaps();
void addMTLPixelFormatDescImpl(MTLPixelFormat p_pix_fmt, MTLPixelFormat p_pix_fmt_linear,
MTLViewClass p_view_class, MTLFmtCaps p_fmt_caps, const char *p_name);
void addMTLVertexFormatDescImpl(MTLVertexFormat p_vert_fmt, MTLFmtCaps p_vert_caps, const char *name);
id<MTLDevice> device;
InflectionMap<DataFormat, DataFormatDesc, RD::DATA_FORMAT_MAX> _data_format_descs;
InflectionMap<uint16_t, MTLFormatDesc, MTLPixelFormatX32_Stencil8 + 2> _mtl_pixel_format_descs; // The actual last enum value is not available on iOS.
TightLocalVector<MTLFormatDesc> _mtl_vertex_format_descs;
};
#pragma clang diagnostic pop
#endif // PIXEL_FORMATS_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,147 @@
/**************************************************************************/
/* rendering_context_driver_metal.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_METAL_H
#define RENDERING_CONTEXT_DRIVER_METAL_H
#ifdef METAL_ENABLED
#import "servers/rendering/rendering_context_driver.h"
#import "servers/rendering/rendering_device_driver.h"
#import <CoreGraphics/CGGeometry.h>
#ifdef __OBJC__
#import "metal_objects.h"
#import <Metal/Metal.h>
#import <QuartzCore/CALayer.h>
@class CAMetalLayer;
@protocol CAMetalDrawable;
#else
typedef enum MTLPixelFormat {
MTLPixelFormatBGRA8Unorm = 80,
} MTLPixelFormat;
class MDCommandBuffer;
#endif
class PixelFormats;
class MDResourceCache;
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingContextDriverMetal : public RenderingContextDriver {
protected:
#ifdef __OBJC__
id<MTLDevice> metal_device = nullptr;
#else
void *metal_device = nullptr;
#endif
Device device; // There is only one device on Apple Silicon.
public:
Error initialize() final override;
const Device &device_get(uint32_t p_device_index) const final override;
uint32_t device_get_count() const final override;
bool device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const final override { return true; }
RenderingDeviceDriver *driver_create() final override;
void driver_free(RenderingDeviceDriver *p_driver) final override;
SurfaceID surface_create(const void *p_platform_data) final override;
void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) final override;
void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) final override;
DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const final override;
uint32_t surface_get_width(SurfaceID p_surface) const final override;
uint32_t surface_get_height(SurfaceID p_surface) const final override;
void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) final override;
bool surface_get_needs_resize(SurfaceID p_surface) const final override;
void surface_destroy(SurfaceID p_surface) final override;
bool is_debug_utils_enabled() const final override { return true; }
#pragma mark - Metal-specific methods
// Platform-specific data for the Windows embedded in this driver.
struct WindowPlatformData {
#ifdef __OBJC__
CAMetalLayer *__unsafe_unretained layer;
#else
void *layer;
#endif
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) Surface {
protected:
#ifdef __OBJC__
id<MTLDevice> device;
#else
void *device;
#endif
public:
uint32_t width = 0;
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
bool needs_resize = false;
double present_minimum_duration = 0.0;
Surface(
#ifdef __OBJC__
id<MTLDevice> p_device
#else
void *p_device
#endif
) :
device(p_device) {
}
virtual ~Surface() = default;
MTLPixelFormat get_pixel_format() const { return MTLPixelFormatBGRA8Unorm; }
virtual Error resize(uint32_t p_desired_framebuffer_count) = 0;
virtual RDD::FramebufferID acquire_next_frame_buffer() = 0;
virtual void present(MDCommandBuffer *p_cmd_buffer) = 0;
void set_max_fps(int p_max_fps) { present_minimum_duration = p_max_fps ? 1.0 / p_max_fps : 0.0; }
};
#ifdef __OBJC__
id<MTLDevice>
#else
void *
#endif
get_metal_device() const {
return metal_device;
}
#pragma mark - Initialization
RenderingContextDriverMetal();
~RenderingContextDriverMetal() override;
};
#endif // METAL_ENABLED
#endif // RENDERING_CONTEXT_DRIVER_METAL_H

View file

@ -0,0 +1,237 @@
/**************************************************************************/
/* rendering_context_driver_metal.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "rendering_context_driver_metal.h"
#import "rendering_device_driver_metal.h"
@protocol MTLDeviceEx <MTLDevice>
#if TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 130300
- (void)setShouldMaximizeConcurrentCompilation:(BOOL)v;
#endif
@end
RenderingContextDriverMetal::RenderingContextDriverMetal() {
}
RenderingContextDriverMetal::~RenderingContextDriverMetal() {
}
Error RenderingContextDriverMetal::initialize() {
metal_device = MTLCreateSystemDefaultDevice();
#if TARGET_OS_OSX
if (@available(macOS 13.3, *)) {
[id<MTLDeviceEx>(metal_device) setShouldMaximizeConcurrentCompilation:YES];
}
#endif
device.type = DEVICE_TYPE_INTEGRATED_GPU;
device.vendor = Vendor::VENDOR_APPLE;
device.workarounds = Workarounds();
MetalDeviceProperties props(metal_device);
int version = (int)props.features.highestFamily - (int)MTLGPUFamilyApple1 + 1;
device.name = vformat("%s (Apple%d)", metal_device.name.UTF8String, version);
return OK;
}
const RenderingContextDriver::Device &RenderingContextDriverMetal::device_get(uint32_t p_device_index) const {
DEV_ASSERT(p_device_index < 1);
return device;
}
uint32_t RenderingContextDriverMetal::device_get_count() const {
return 1;
}
RenderingDeviceDriver *RenderingContextDriverMetal::driver_create() {
return memnew(RenderingDeviceDriverMetal(this));
}
void RenderingContextDriverMetal::driver_free(RenderingDeviceDriver *p_driver) {
memdelete(p_driver);
}
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) SurfaceLayer : public RenderingContextDriverMetal::Surface {
CAMetalLayer *__unsafe_unretained layer = nil;
LocalVector<MDFrameBuffer> frame_buffers;
LocalVector<id<MTLDrawable>> drawables;
uint32_t rear = -1;
uint32_t front = 0;
uint32_t count = 0;
public:
SurfaceLayer(CAMetalLayer *p_layer, id<MTLDevice> p_device) :
Surface(p_device), layer(p_layer) {
layer.allowsNextDrawableTimeout = YES;
layer.framebufferOnly = YES;
layer.opaque = OS::get_singleton()->is_layered_allowed() ? NO : YES;
layer.pixelFormat = get_pixel_format();
layer.device = p_device;
}
~SurfaceLayer() override {
layer = nil;
}
Error resize(uint32_t p_desired_framebuffer_count) override final {
if (width == 0 || height == 0) {
// Very likely the window is minimized, don't create a swap chain.
return ERR_SKIP;
}
CGSize drawableSize = CGSizeMake(width, height);
CGSize current = layer.drawableSize;
if (!CGSizeEqualToSize(current, drawableSize)) {
layer.drawableSize = drawableSize;
}
// Metal supports a maximum of 3 drawables.
p_desired_framebuffer_count = MIN(3U, p_desired_framebuffer_count);
layer.maximumDrawableCount = p_desired_framebuffer_count;
#if TARGET_OS_OSX
// Display sync is only supported on macOS.
switch (vsync_mode) {
case DisplayServer::VSYNC_MAILBOX:
case DisplayServer::VSYNC_ADAPTIVE:
case DisplayServer::VSYNC_ENABLED:
layer.displaySyncEnabled = YES;
break;
case DisplayServer::VSYNC_DISABLED:
layer.displaySyncEnabled = NO;
break;
}
#endif
drawables.resize(p_desired_framebuffer_count);
frame_buffers.resize(p_desired_framebuffer_count);
for (uint32_t i = 0; i < p_desired_framebuffer_count; i++) {
// Reserve space for the drawable texture.
frame_buffers[i].set_texture_count(1);
}
return OK;
}
RDD::FramebufferID acquire_next_frame_buffer() override final {
if (count == frame_buffers.size()) {
return RDD::FramebufferID();
}
rear = (rear + 1) % frame_buffers.size();
count++;
MDFrameBuffer &frame_buffer = frame_buffers[rear];
frame_buffer.size = Size2i(width, height);
id<CAMetalDrawable> drawable = layer.nextDrawable;
ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available");
drawables[rear] = drawable;
frame_buffer.set_texture(0, drawable.texture);
return RDD::FramebufferID(&frame_buffer);
}
void present(MDCommandBuffer *p_cmd_buffer) override final {
if (count == 0) {
return;
}
// Release texture and drawable.
frame_buffers[front].unset_texture(0);
id<MTLDrawable> drawable = drawables[front];
drawables[front] = nil;
count--;
front = (front + 1) % frame_buffers.size();
if (vsync_mode != DisplayServer::VSYNC_DISABLED) {
[p_cmd_buffer->get_command_buffer() presentDrawable:drawable afterMinimumDuration:present_minimum_duration];
} else {
[p_cmd_buffer->get_command_buffer() presentDrawable:drawable];
}
}
};
RenderingContextDriver::SurfaceID RenderingContextDriverMetal::surface_create(const void *p_platform_data) {
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
Surface *surface = memnew(SurfaceLayer(wpd->layer, metal_device));
return SurfaceID(surface);
}
void RenderingContextDriverMetal::surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) {
Surface *surface = (Surface *)(p_surface);
if (surface->width == p_width && surface->height == p_height) {
return;
}
surface->width = p_width;
surface->height = p_height;
surface->needs_resize = true;
}
void RenderingContextDriverMetal::surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) {
Surface *surface = (Surface *)(p_surface);
if (surface->vsync_mode == p_vsync_mode) {
return;
}
surface->vsync_mode = p_vsync_mode;
surface->needs_resize = true;
}
DisplayServer::VSyncMode RenderingContextDriverMetal::surface_get_vsync_mode(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->vsync_mode;
}
uint32_t RenderingContextDriverMetal::surface_get_width(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->width;
}
uint32_t RenderingContextDriverMetal::surface_get_height(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->height;
}
void RenderingContextDriverMetal::surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) {
Surface *surface = (Surface *)(p_surface);
surface->needs_resize = p_needs_resize;
}
bool RenderingContextDriverMetal::surface_get_needs_resize(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->needs_resize;
}
void RenderingContextDriverMetal::surface_destroy(SurfaceID p_surface) {
Surface *surface = (Surface *)(p_surface);
memdelete(surface);
}

View file

@ -0,0 +1,448 @@
/**************************************************************************/
/* rendering_device_driver_metal.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_DEVICE_DRIVER_METAL_H
#define RENDERING_DEVICE_DRIVER_METAL_H
#import "metal_objects.h"
#import "servers/rendering/rendering_device_driver.h"
#import <Metal/Metal.h>
#import <variant>
#ifdef DEBUG_ENABLED
#ifndef _DEBUG
#define _DEBUG
#endif
#endif
class RenderingContextDriverMetal;
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMetal : public RenderingDeviceDriver {
friend struct ShaderCacheEntry;
template <typename T>
using Result = std::variant<T, Error>;
#pragma mark - Generic
RenderingContextDriverMetal *context_driver = nullptr;
RenderingContextDriver::Device context_device;
id<MTLDevice> device = nil;
uint32_t version_major = 2;
uint32_t version_minor = 0;
MetalDeviceProperties *device_properties = nullptr;
PixelFormats *pixel_formats = nullptr;
std::unique_ptr<MDResourceCache> resource_cache;
RDD::Capabilities capabilities;
RDD::MultiviewCapabilities multiview_capabilities;
id<MTLBinaryArchive> archive = nil;
uint32_t archive_count = 0;
id<MTLCommandQueue> device_queue = nil;
id<MTLCaptureScope> device_scope = nil;
String pipeline_cache_id;
Error _create_device();
Error _check_capabilities();
#pragma mark - Shader Cache
ShaderLoadStrategy _shader_load_strategy = ShaderLoadStrategy::DEFAULT;
/**
* The shader cache is a map of hashes of the Metal source to shader cache entries.
*
* To prevent unbounded growth of the cache, cache entries are automatically freed when
* there are no more references to the MDLibrary associated with the cache entry.
*/
HashMap<SHA256Digest, ShaderCacheEntry *, HashableHasher<SHA256Digest>> _shader_cache;
void shader_cache_free_entry(const SHA256Digest &key);
public:
Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override final;
#pragma mark - Memory
#pragma mark - Buffers
public:
virtual BufferID buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) override final;
virtual bool buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) override final;
virtual void buffer_free(BufferID p_buffer) override final;
virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final;
virtual uint8_t *buffer_map(BufferID p_buffer) override final;
virtual void buffer_unmap(BufferID p_buffer) override final;
virtual uint64_t buffer_get_device_address(BufferID p_buffer) override final;
#pragma mark - Texture
private:
// Returns true if the texture is a valid linear format.
Result<bool> is_valid_linear(TextureFormat const &p_format) const;
void _get_sub_resource(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) const;
public:
virtual TextureID texture_create(const TextureFormat &p_format, const TextureView &p_view) override final;
virtual TextureID texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) override final;
virtual TextureID texture_create_shared(TextureID p_original_texture, const TextureView &p_view) override final;
virtual TextureID texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) override final;
virtual void texture_free(TextureID p_texture) override final;
virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final;
virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final;
virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) override final;
virtual void texture_unmap(TextureID p_texture) override final;
virtual BitField<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final;
virtual bool texture_can_make_shared_with_format(TextureID p_texture, DataFormat p_format, bool &r_raw_reinterpretation) override final;
#pragma mark - Sampler
public:
virtual SamplerID sampler_create(const SamplerState &p_state) final override;
virtual void sampler_free(SamplerID p_sampler) final override;
virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) override final;
#pragma mark - Vertex Array
private:
public:
virtual VertexFormatID vertex_format_create(VectorView<VertexAttribute> p_vertex_attribs) override final;
virtual void vertex_format_free(VertexFormatID p_vertex_format) override final;
#pragma mark - Barriers
virtual void command_pipeline_barrier(
CommandBufferID p_cmd_buffer,
BitField<PipelineStageBits> p_src_stages,
BitField<PipelineStageBits> p_dst_stages,
VectorView<MemoryBarrier> p_memory_barriers,
VectorView<BufferBarrier> p_buffer_barriers,
VectorView<TextureBarrier> p_texture_barriers) override final;
#pragma mark - Fences
private:
struct Fence {
dispatch_semaphore_t semaphore;
Fence() :
semaphore(dispatch_semaphore_create(0)) {}
};
public:
virtual FenceID fence_create() override final;
virtual Error fence_wait(FenceID p_fence) override final;
virtual void fence_free(FenceID p_fence) override final;
#pragma mark - Semaphores
public:
virtual SemaphoreID semaphore_create() override final;
virtual void semaphore_free(SemaphoreID p_semaphore) override final;
#pragma mark - Commands
// ----- QUEUE FAMILY -----
virtual CommandQueueFamilyID command_queue_family_get(BitField<CommandQueueFamilyBits> p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface = 0) override final;
// ----- QUEUE -----
public:
virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override final;
virtual Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override final;
virtual void command_queue_free(CommandQueueID p_cmd_queue) override final;
// ----- POOL -----
virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override final;
virtual bool command_pool_reset(CommandPoolID p_cmd_pool) override final;
virtual void command_pool_free(CommandPoolID p_cmd_pool) override final;
// ----- BUFFER -----
private:
// Used to maintain references.
Vector<MDCommandBuffer *> command_buffers;
public:
virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override final;
virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) override final;
virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final;
virtual void command_buffer_end(CommandBufferID p_cmd_buffer) override final;
virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> p_secondary_cmd_buffers) override final;
#pragma mark - Swapchain
private:
struct SwapChain {
RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID();
RenderPassID render_pass;
RDD::DataFormat data_format = DATA_FORMAT_MAX;
SwapChain() :
render_pass(nullptr) {}
};
void _swap_chain_release(SwapChain *p_swap_chain);
void _swap_chain_release_buffers(SwapChain *p_swap_chain);
public:
virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) override final;
virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) override final;
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override final;
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final;
virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final;
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
#pragma mark - Frame Buffer
virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) override final;
virtual void framebuffer_free(FramebufferID p_framebuffer) override final;
#pragma mark - Shader
private:
// Serialization types need access to private state.
friend struct ShaderStageData;
friend struct SpecializationConstantData;
friend struct UniformData;
friend struct ShaderBinaryData;
friend struct PushConstantData;
private:
/// Contains additional metadata about the shader.
struct ShaderMeta {
/// Indicates whether the shader uses multiview.
bool has_multiview = false;
};
Error _reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection, ShaderMeta &r_shader_meta);
public:
virtual String shader_get_binary_cache_key() override final;
virtual Vector<uint8_t> shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) override final;
virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name, const Vector<ImmutableSampler> &p_immutable_samplers) override final;
virtual void shader_free(ShaderID p_shader) override final;
virtual void shader_destroy_modules(ShaderID p_shader) override final;
#pragma mark - Uniform Set
public:
virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index, int p_linear_pool_index) override final;
virtual void uniform_set_free(UniformSetID p_uniform_set) override final;
#pragma mark - Commands
virtual void command_uniform_set_prepare_for_use(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
#pragma mark Transfer
private:
enum class CopySource {
Buffer,
Texture,
};
void _copy_texture_buffer(CommandBufferID p_cmd_buffer,
CopySource p_source,
TextureID p_texture,
BufferID p_buffer,
VectorView<BufferTextureCopyRegion> p_regions);
public:
virtual void command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) override final;
virtual void command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) override final;
virtual void command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) override final;
virtual void command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) override final;
virtual void command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) override final;
virtual void command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) override final;
virtual void command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) override final;
#pragma mark Pipeline
private:
Result<id<MTLFunction>> _create_function(MDLibrary *p_library, NSString *p_name, VectorView<PipelineSpecializationConstant> &p_specialization_constants);
public:
virtual void pipeline_free(PipelineID p_pipeline_id) override final;
// ----- BINDING -----
virtual void command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_first_index, VectorView<uint32_t> p_data) override final;
// ----- CACHE -----
private:
String _pipeline_get_cache_path() const;
public:
virtual bool pipeline_cache_create(const Vector<uint8_t> &p_data) override final;
virtual void pipeline_cache_free() override final;
virtual size_t pipeline_cache_query_size() override final;
virtual Vector<uint8_t> pipeline_cache_serialize() override final;
#pragma mark Rendering
// ----- SUBPASS -----
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) override final;
virtual void render_pass_free(RenderPassID p_render_pass) override final;
// ----- COMMANDS -----
public:
virtual void command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) override final;
virtual void command_end_render_pass(CommandBufferID p_cmd_buffer) override final;
virtual void command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) override final;
virtual void command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) override final;
virtual void command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) override final;
virtual void command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) override final;
// Binding.
virtual void command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
virtual void command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
virtual void command_bind_render_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count) override final;
// Drawing.
virtual void command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) override final;
virtual void command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) override final;
virtual void command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
virtual void command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
// Buffer binding.
virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) override final;
virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) override final;
// Dynamic state.
virtual void command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) override final;
virtual void command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) override final;
// ----- PIPELINE -----
virtual PipelineID render_pipeline_create(
ShaderID p_shader,
VertexFormatID p_vertex_format,
RenderPrimitive p_render_primitive,
PipelineRasterizationState p_rasterization_state,
PipelineMultisampleState p_multisample_state,
PipelineDepthStencilState p_depth_stencil_state,
PipelineColorBlendState p_blend_state,
VectorView<int32_t> p_color_attachments,
BitField<PipelineDynamicStateFlags> p_dynamic_state,
RenderPassID p_render_pass,
uint32_t p_render_subpass,
VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
#pragma mark - Compute
// ----- COMMANDS -----
// Binding.
virtual void command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
virtual void command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
virtual void command_bind_compute_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count) override final;
// Dispatching.
virtual void command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final;
virtual void command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) override final;
// ----- PIPELINE -----
virtual PipelineID compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
#pragma mark - Queries
// ----- TIMESTAMP -----
// Basic.
virtual QueryPoolID timestamp_query_pool_create(uint32_t p_query_count) override final;
virtual void timestamp_query_pool_free(QueryPoolID p_pool_id) override final;
virtual void timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) override final;
virtual uint64_t timestamp_query_result_to_time(uint64_t p_result) override final;
// Commands.
virtual void command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) override final;
virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) override final;
#pragma mark - Labels
virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final;
virtual void command_end_label(CommandBufferID p_cmd_buffer) override final;
#pragma mark - Debug
virtual void command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) override final;
#pragma mark - Submission
virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final;
virtual void end_segment() override final;
#pragma mark - Miscellaneous
virtual void set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) override final;
virtual uint64_t get_resource_native_handle(DriverResource p_type, ID p_driver_id) override final;
virtual uint64_t get_total_memory_used() override final;
virtual uint64_t get_lazily_memory_used() override final;
virtual uint64_t limit_get(Limit p_limit) override final;
virtual uint64_t api_trait_get(ApiTrait p_trait) override final;
virtual bool has_feature(Features p_feature) override final;
virtual const MultiviewCapabilities &get_multiview_capabilities() override final;
virtual String get_api_name() const override final { return "Metal"; }
virtual String get_api_version() const override final;
virtual String get_pipeline_cache_uuid() const override final;
virtual const Capabilities &get_capabilities() const override final;
virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
// Metal-specific.
id<MTLDevice> get_device() const { return device; }
PixelFormats &get_pixel_formats() const { return *pixel_formats; }
MDResourceCache &get_resource_cache() const { return *resource_cache; }
MetalDeviceProperties const &get_device_properties() const { return *device_properties; }
_FORCE_INLINE_ uint32_t get_metal_buffer_index_for_vertex_attribute_binding(uint32_t p_binding) {
return (device_properties->limits.maxPerStageBufferCount - 1) - p_binding;
}
size_t get_texel_buffer_alignment_for_format(RDD::DataFormat p_format) const;
size_t get_texel_buffer_alignment_for_format(MTLPixelFormat p_format) const;
/******************/
RenderingDeviceDriverMetal(RenderingContextDriverMetal *p_context_driver);
~RenderingDeviceDriverMetal();
};
#endif // RENDERING_DEVICE_DRIVER_METAL_H

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
@ -45,18 +46,20 @@ if env["builtin_libpng"]:
if "S_compiler" in env:
env_neon["CC"] = env["S_compiler"]
neon_sources = []
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/arm_init.c"))
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c"))
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S"))
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/palette_neon_intrinsics.c"))
neon_sources.append(env_neon.Object(thirdparty_dir + "arm/arm_init.c"))
neon_sources.append(env_neon.Object(thirdparty_dir + "arm/filter_neon_intrinsics.c"))
neon_sources.append(env_neon.Object(thirdparty_dir + "arm/palette_neon_intrinsics.c"))
thirdparty_obj += neon_sources
elif env["arch"].startswith("x86"):
env_thirdparty.Append(CPPDEFINES=["PNG_INTEL_SSE"])
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/intel_init.c")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/filter_sse2_intrinsics.c")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "intel/intel_init.c")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "intel/filter_sse2_intrinsics.c")
elif env["arch"] == "loongarch64":
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "loongarch/loongarch_lsx_init.c")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "loongarch/filter_lsx_intrinsics.c")
elif env["arch"] == "ppc64":
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/powerpc_init.c")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/filter_vsx_intrinsics.c")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "powerpc/powerpc_init.c")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "powerpc/filter_vsx_intrinsics.c")
env.drivers_sources += thirdparty_obj

View file

@ -30,8 +30,6 @@
#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>

View file

@ -30,7 +30,7 @@
#include "png_driver_common.h"
#include "core/os/os.h"
#include "core/config/engine.h"
#include <png.h>
#include <string.h>

View file

@ -38,7 +38,7 @@
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.is_null(), 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();

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -725,7 +725,8 @@ Error AudioDriverPulseAudio::init_input_device() {
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;
pa_buffer_attr attr = {};
attr.maxlength = (uint32_t)-1;
attr.fragsize = input_buffer_size * sizeof(int16_t);
pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map);

View file

@ -100,7 +100,7 @@ class AudioDriverPulseAudio : public AudioDriver {
public:
virtual const char *get_name() const override {
return "PulseAudio";
};
}
virtual Error init() override;
virtual void start() override;

View file

@ -30,7 +30,6 @@
#include "register_driver_types.h"
#include "core/extension/gdextension_manager.h"
#include "drivers/png/image_loader_png.h"
#include "drivers/png/resource_saver_png.h"

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -397,14 +397,30 @@ Error DirAccessUnix::rename(String p_path, String p_new_path) {
}
p_path = fix_path(p_path);
if (p_path.ends_with("/")) {
p_path = p_path.left(-1);
}
if (p_new_path.is_relative_path()) {
p_new_path = get_current_dir().path_join(p_new_path);
}
p_new_path = fix_path(p_new_path);
if (p_new_path.ends_with("/")) {
p_new_path = p_new_path.left(-1);
}
return ::rename(p_path.utf8().get_data(), p_new_path.utf8().get_data()) == 0 ? OK : FAILED;
int res = ::rename(p_path.utf8().get_data(), p_new_path.utf8().get_data());
if (res != 0 && errno == EXDEV) { // Cross-device move, use copy and remove.
Error err = OK;
err = copy(p_path, p_new_path);
if (err != OK) {
return err;
}
return remove(p_path);
} else {
return (res == 0) ? OK : FAILED;
}
}
Error DirAccessUnix::remove(String p_path) {
@ -413,17 +429,28 @@ Error DirAccessUnix::remove(String p_path) {
}
p_path = fix_path(p_path);
if (p_path.ends_with("/")) {
p_path = p_path.left(-1);
}
struct stat flags = {};
if ((stat(p_path.utf8().get_data(), &flags) != 0)) {
if ((lstat(p_path.utf8().get_data(), &flags) != 0)) {
return FAILED;
}
int err;
if (S_ISDIR(flags.st_mode) && !is_link(p_path)) {
return ::rmdir(p_path.utf8().get_data()) == 0 ? OK : FAILED;
err = ::rmdir(p_path.utf8().get_data());
} else {
return ::unlink(p_path.utf8().get_data()) == 0 ? OK : FAILED;
err = ::unlink(p_path.utf8().get_data());
}
if (err != 0) {
return FAILED;
}
if (remove_notification_func != nullptr) {
remove_notification_func(p_path);
}
return OK;
}
bool DirAccessUnix::is_link(String p_file) {
@ -432,6 +459,9 @@ bool DirAccessUnix::is_link(String p_file) {
}
p_file = fix_path(p_file);
if (p_file.ends_with("/")) {
p_file = p_file.left(-1);
}
struct stat flags = {};
if ((lstat(p_file.utf8().get_data(), &flags) != 0)) {
@ -447,6 +477,9 @@ String DirAccessUnix::read_link(String p_file) {
}
p_file = fix_path(p_file);
if (p_file.ends_with("/")) {
p_file = p_file.left(-1);
}
char buf[256];
memset(buf, 0, 256);
@ -527,6 +560,8 @@ DirAccessUnix::DirAccessUnix() {
change_dir(current_dir);
}
DirAccessUnix::RemoveNotificationFunc DirAccessUnix::remove_notification_func = nullptr;
DirAccessUnix::~DirAccessUnix() {
list_dir_end();
}

View file

@ -52,6 +52,9 @@ protected:
virtual bool is_hidden(const String &p_name);
public:
typedef void (*RemoveNotificationFunc)(const String &p_file);
static RemoveNotificationFunc remove_notification_func;
virtual Error list_dir_begin() override; ///< This starts dir listing
virtual String get_next() override;
virtual bool current_is_dir() const override;

View file

@ -41,10 +41,23 @@
#include <sys/types.h>
#include <unistd.h>
void FileAccessUnix::check_errors() const {
#if defined(TOOLS_ENABLED)
#include <limits.h>
#include <stdlib.h>
#endif
void FileAccessUnix::check_errors(bool p_write) const {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
if (feof(f)) {
last_error = OK;
if (ferror(f)) {
if (p_write) {
last_error = ERR_FILE_CANT_WRITE;
} else {
last_error = ERR_FILE_CANT_READ;
}
}
if (!p_write && feof(f)) {
last_error = ERR_FILE_EOF;
}
}
@ -87,6 +100,22 @@ Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) {
}
}
#if defined(TOOLS_ENABLED)
if (p_mode_flags & READ) {
String real_path = get_real_path();
if (real_path != path) {
// Don't warn on symlinks, since they can be used to simply share addons on multiple projects.
if (real_path.to_lower() == path.to_lower()) {
// The File system is case insensitive, but other platforms can be sensitive to it
// To ease cross-platform development, we issue a warning if users try to access
// a file using the wrong case (which *works* on Windows and macOS, but won't on other
// platforms).
WARN_PRINT(vformat("Case mismatch opening requested file '%s', stored as '%s' in the filesystem. This file will not open when exported to other case-sensitive platforms.", path, real_path));
}
}
}
#endif
if (is_backup_save_enabled() && (p_mode_flags == WRITE)) {
save_path = path;
// Create a temporary file in the same directory as the target file.
@ -97,7 +126,7 @@ Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) {
last_error = ERR_FILE_CANT_OPEN;
return last_error;
}
fchmod(fd, 0666);
fchmod(fd, 0644);
path = String::utf8(cs.ptr());
f = fdopen(fd, mode_string);
@ -173,10 +202,29 @@ String FileAccessUnix::get_path_absolute() const {
return path;
}
#if defined(TOOLS_ENABLED)
String FileAccessUnix::get_real_path() const {
char *resolved_path = ::realpath(path.utf8().get_data(), nullptr);
if (!resolved_path) {
return path;
}
String result;
Error parse_ok = result.parse_utf8(resolved_path);
::free(resolved_path);
if (parse_ok != OK) {
return path;
}
return result.simplify_path();
}
#endif
void FileAccessUnix::seek(uint64_t p_position) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
last_error = OK;
if (fseeko(f, p_position, SEEK_SET)) {
check_errors();
}
@ -215,70 +263,16 @@ uint64_t FileAccessUnix::get_length() const {
}
bool FileAccessUnix::eof_reached() const {
return last_error == ERR_FILE_EOF;
}
uint8_t FileAccessUnix::get_8() const {
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
uint8_t b;
if (fread(&b, 1, 1, f) == 0) {
check_errors();
b = '\0';
}
return b;
}
uint16_t FileAccessUnix::get_16() const {
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
uint16_t b = 0;
if (fread(&b, 1, 2, f) != 2) {
check_errors();
}
if (big_endian) {
b = BSWAP16(b);
}
return b;
}
uint32_t FileAccessUnix::get_32() const {
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
uint32_t b = 0;
if (fread(&b, 1, 4, f) != 4) {
check_errors();
}
if (big_endian) {
b = BSWAP32(b);
}
return b;
}
uint64_t FileAccessUnix::get_64() const {
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
uint64_t b = 0;
if (fread(&b, 1, 8, f) != 8) {
check_errors();
}
if (big_endian) {
b = BSWAP64(b);
}
return b;
return feof(f);
}
uint64_t FileAccessUnix::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_NULL_V_MSG(f, -1, "File must be opened before use.");
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
uint64_t read = fread(p_dst, 1, p_length, f);
check_errors();
return read;
}
@ -308,60 +302,25 @@ void FileAccessUnix::flush() {
fflush(f);
}
void FileAccessUnix::store_8(uint8_t p_dest) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
ERR_FAIL_COND(fwrite(&p_dest, 1, 1, f) != 1);
}
void FileAccessUnix::store_16(uint16_t p_dest) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
if (big_endian) {
p_dest = BSWAP16(p_dest);
}
ERR_FAIL_COND(fwrite(&p_dest, 1, 2, f) != 2);
}
void FileAccessUnix::store_32(uint32_t p_dest) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
if (big_endian) {
p_dest = BSWAP32(p_dest);
}
ERR_FAIL_COND(fwrite(&p_dest, 1, 4, f) != 4);
}
void FileAccessUnix::store_64(uint64_t p_dest) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
if (big_endian) {
p_dest = BSWAP64(p_dest);
}
ERR_FAIL_COND(fwrite(&p_dest, 1, 8, f) != 8);
}
void FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
ERR_FAIL_COND(!p_src && p_length > 0);
ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != p_length);
bool FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_NULL_V_MSG(f, false, "File must be opened before use.");
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
bool res = fwrite(p_src, 1, p_length, f) == p_length;
check_errors(true);
return res;
}
bool FileAccessUnix::file_exists(const String &p_path) {
int err;
struct stat st = {};
String filename = fix_path(p_path);
const CharString filename_utf8 = fix_path(p_path).utf8();
// Does the name exist at all?
err = stat(filename.utf8().get_data(), &st);
if (err) {
if (stat(filename_utf8.get_data(), &st)) {
return false;
}
// See if we have access to the file
if (access(filename.utf8().get_data(), F_OK)) {
if (access(filename_utf8.get_data(), F_OK)) {
return false;
}
@ -381,9 +340,18 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) {
int err = stat(file.utf8().get_data(), &status);
if (!err) {
return status.st_mtime;
uint64_t modified_time = status.st_mtime;
#ifdef ANDROID_ENABLED
// Workaround for GH-101007
//FIXME: After saving, all timestamps (st_mtime, st_ctime, st_atime) are set to the same value.
// After exporting or after some time, only 'modified_time' resets to a past timestamp.
uint64_t created_time = status.st_ctime;
if (modified_time < created_time) {
modified_time = created_time;
}
#endif
return modified_time;
} else {
print_verbose("Failed to get modified time for: " + p_file + "");
return 0;
}
}
@ -483,7 +451,7 @@ void FileAccessUnix::close() {
_close();
}
CloseNotificationFunc FileAccessUnix::close_notification_func = nullptr;
FileAccessUnix::CloseNotificationFunc FileAccessUnix::close_notification_func = nullptr;
FileAccessUnix::~FileAccessUnix() {
_close();

View file

@ -38,12 +38,10 @@
#if defined(UNIX_ENABLED)
typedef void (*CloseNotificationFunc)(const String &p_file, int p_flags);
class FileAccessUnix : public FileAccess {
FILE *f = nullptr;
int flags = 0;
void check_errors() const;
void check_errors(bool p_write = false) const;
mutable Error last_error = OK;
String save_path;
String path;
@ -51,7 +49,12 @@ class FileAccessUnix : public FileAccess {
void _close();
#if defined(TOOLS_ENABLED)
String get_real_path() const; // Returns the resolved real path for the current open file.
#endif
public:
typedef void (*CloseNotificationFunc)(const String &p_file, int p_flags);
static CloseNotificationFunc close_notification_func;
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
@ -67,21 +70,13 @@ public:
virtual bool eof_reached() const override; ///< reading passed EOF
virtual uint8_t get_8() const override; ///< get a byte
virtual uint16_t get_16() const override;
virtual uint32_t get_32() const override;
virtual uint64_t get_64() const override;
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override;
virtual void flush() override;
virtual void store_8(uint8_t p_dest) override; ///< store a byte
virtual void store_16(uint16_t p_dest) override;
virtual void store_32(uint32_t p_dest) override;
virtual void store_64(uint64_t p_dest) override;
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool file_exists(const String &p_path) override; ///< return true if a file exists

View file

@ -37,11 +37,12 @@
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd) {
Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd, bool p_blocking) {
// Open pipe using handles created by pipe(fd) call in the OS.execute_with_pipe.
_close();
@ -51,6 +52,11 @@ Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd) {
fd[0] = p_rfd;
fd[1] = p_wfd;
if (!p_blocking) {
fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_NONBLOCK);
fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL) | O_NONBLOCK);
}
last_error = OK;
return OK;
}
@ -62,10 +68,12 @@ Error FileAccessUnixPipe::open_internal(const String &p_path, int p_mode_flags)
ERR_FAIL_COND_V_MSG(fd[0] >= 0 || fd[1] >= 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
path = String("/tmp/") + p_path.replace("pipe://", "").replace("/", "_");
const CharString path_utf8 = path.utf8();
struct stat st = {};
int err = stat(path.utf8().get_data(), &st);
int err = stat(path_utf8.get_data(), &st);
if (err) {
if (mkfifo(path.utf8().get_data(), 0666) != 0) {
if (mkfifo(path_utf8.get_data(), 0600) != 0) {
last_error = ERR_FILE_CANT_OPEN;
return last_error;
}
@ -74,7 +82,7 @@ Error FileAccessUnixPipe::open_internal(const String &p_path, int p_mode_flags)
ERR_FAIL_COND_V_MSG(!S_ISFIFO(st.st_mode), ERR_ALREADY_IN_USE, "Pipe name is already used by file.");
}
int f = ::open(path.utf8().get_data(), O_RDWR | O_CLOEXEC);
int f = ::open(path_utf8.get_data(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (f < 0) {
switch (errno) {
case ENOENT: {
@ -125,25 +133,23 @@ String FileAccessUnixPipe::get_path_absolute() const {
return path_src;
}
uint8_t FileAccessUnixPipe::get_8() const {
uint64_t FileAccessUnixPipe::get_length() const {
ERR_FAIL_COND_V_MSG(fd[0] < 0, 0, "Pipe must be opened before use.");
uint8_t b;
if (::read(fd[0], &b, 1) == 0) {
last_error = ERR_FILE_CANT_READ;
b = '\0';
} else {
last_error = OK;
}
return b;
int buf_rem = 0;
ERR_FAIL_COND_V(ioctl(fd[0], FIONREAD, &buf_rem) != 0, 0);
return buf_rem;
}
uint64_t FileAccessUnixPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V_MSG(fd[0] < 0, -1, "Pipe must be opened before use.");
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
uint64_t read = ::read(fd[0], p_dst, p_length);
if (read == p_length) {
ssize_t read = ::read(fd[0], p_dst, p_length);
if (read == -1) {
last_error = ERR_FILE_CANT_READ;
read = 0;
} else if (read != (ssize_t)p_length) {
last_error = ERR_FILE_CANT_READ;
} else {
last_error = OK;
@ -155,22 +161,16 @@ Error FileAccessUnixPipe::get_error() const {
return last_error;
}
void FileAccessUnixPipe::store_8(uint8_t p_src) {
ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
if (::write(fd[1], &p_src, 1) != 1) {
last_error = ERR_FILE_CANT_WRITE;
} else {
last_error = OK;
}
}
bool FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_V_MSG(fd[1] < 0, false, "Pipe must be opened before use.");
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
void FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
ERR_FAIL_COND(!p_src && p_length > 0);
if (::write(fd[1], p_src, p_length) != (ssize_t)p_length) {
last_error = ERR_FILE_CANT_WRITE;
return false;
} else {
last_error = OK;
return true;
}
}

View file

@ -50,7 +50,7 @@ class FileAccessUnixPipe : public FileAccess {
void _close();
public:
Error open_existing(int p_rfd, int p_wfd);
Error open_existing(int p_rfd, int p_wfd, bool p_blocking);
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open
@ -61,19 +61,17 @@ public:
virtual void seek(uint64_t p_position) override {}
virtual void seek_end(int64_t p_position = 0) override {}
virtual uint64_t get_position() const override { return 0; }
virtual uint64_t get_length() const override { return 0; }
virtual uint64_t get_length() const override;
virtual bool eof_reached() const override { return false; }
virtual uint8_t get_8() const override; ///< get a byte
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
virtual Error get_error() const override; ///< get last error
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
virtual void flush() override {}
virtual void store_8(uint8_t p_src) override; ///< store a byte
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
virtual bool file_exists(const String &p_path) override { return false; }

View file

@ -28,23 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE)
#include "ip_unix.h"
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
#ifdef WINDOWS_ENABLED
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#else // UNIX
#include <netdb.h>
#ifdef ANDROID_ENABLED
@ -67,8 +54,6 @@
#include <net/if.h> // Order is important on OpenBSD, leave as last.
#endif // UNIX
#include <string.h>
static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
@ -108,7 +93,7 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
}
if (result == nullptr || result->ai_addr == nullptr) {
print_verbose("Invalid response from getaddrinfo");
print_verbose("Invalid response from getaddrinfo.");
if (result) {
freeaddrinfo(result);
}
@ -132,56 +117,6 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
freeaddrinfo(result);
}
#if defined(WINDOWS_ENABLED)
void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
ULONG buf_size = 1024;
IP_ADAPTER_ADDRESSES *addrs;
while (true) {
addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
nullptr, addrs, &buf_size);
if (err == NO_ERROR) {
break;
}
memfree(addrs);
if (err == ERROR_BUFFER_OVERFLOW) {
continue; // will go back and alloc the right size
}
ERR_FAIL_MSG("Call to GetAdaptersAddresses failed with error " + itos(err) + ".");
}
IP_ADAPTER_ADDRESSES *adapter = addrs;
while (adapter != nullptr) {
Interface_Info info;
info.name = adapter->AdapterName;
info.name_friendly = adapter->FriendlyName;
info.index = String::num_uint64(adapter->IfIndex);
IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
while (address != nullptr) {
int family = address->Address.lpSockaddr->sa_family;
if (family != AF_INET && family != AF_INET6) {
continue;
}
info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr));
address = address->Next;
}
adapter = adapter->Next;
// Only add interface if it has at least one IP
if (info.ip_addresses.size() > 0) {
r_interfaces->insert(info.name, info);
}
}
memfree(addrs);
}
#else // UNIX
void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
struct ifaddrs *ifAddrStruct = nullptr;
struct ifaddrs *ifa = nullptr;
@ -219,8 +154,6 @@ void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces)
}
}
#endif // UNIX
void IPUnix::make_default() {
_create = _create_unix;
}
@ -232,4 +165,4 @@ IP *IPUnix::_create_unix() {
IPUnix::IPUnix() {
}
#endif // UNIX_ENABLED || WINDOWS_ENABLED
#endif // UNIX_ENABLED

View file

@ -31,9 +31,9 @@
#ifndef IP_UNIX_H
#define IP_UNIX_H
#include "core/io/ip.h"
#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE)
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
#include "core/io/ip.h"
class IPUnix : public IP {
GDCLASS(IPUnix, IP);
@ -49,6 +49,6 @@ public:
IPUnix();
};
#endif
#endif // UNIX_ENABLED
#endif // IP_UNIX_H

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