diff options
Diffstat (limited to 'drivers')
87 files changed, 18689 insertions, 2207 deletions
diff --git a/drivers/SCsub b/drivers/SCsub index e2ac9ee01e..dd81fc645c 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -23,10 +23,11 @@ SConscript("coremidi/SCsub") SConscript("winmidi/SCsub") # Graphics drivers -if env["platform"] != "server" and env["platform"] != "javascript": +if env["vulkan"]: SConscript("vulkan/SCsub") -else: - SConscript("dummy/SCsub") +if env["opengl3"]: + SConscript("gl_context/SCsub") + SConscript("gles3/SCsub") # Core dependencies SConscript("png/SCsub") diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index 61475c74e7..7884269103 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -283,9 +283,9 @@ Array AudioDriverALSA::get_device_list() { if (name != nullptr && !strncmp(name, "plughw", 6)) { if (desc) { - list.push_back(String(name) + ";" + String(desc)); + list.push_back(String::utf8(name) + ";" + String::utf8(desc)); } else { - list.push_back(String(name)); + list.push_back(String::utf8(name)); } } diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h index ca97e76bc2..dbb40fa088 100644 --- a/drivers/alsa/audio_driver_alsa.h +++ b/drivers/alsa/audio_driver_alsa.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/alsamidi/midi_driver_alsamidi.cpp b/drivers/alsamidi/midi_driver_alsamidi.cpp index 245ea07730..0674c90cd6 100644 --- a/drivers/alsamidi/midi_driver_alsamidi.cpp +++ b/drivers/alsamidi/midi_driver_alsamidi.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/alsamidi/midi_driver_alsamidi.h b/drivers/alsamidi/midi_driver_alsamidi.h index c327712ee7..b0fa8c297a 100644 --- a/drivers/alsamidi/midi_driver_alsamidi.h +++ b/drivers/alsamidi/midi_driver_alsamidi.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index baa60f5526..e2b195350f 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -70,7 +70,7 @@ OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID Error AudioDriverCoreAudio::init() { AudioComponentDescription desc; - zeromem(&desc, sizeof(desc)); + memset(&desc, 0, sizeof(desc)); desc.componentType = kAudioUnitType_Output; #ifdef OSX_ENABLED desc.componentSubType = kAudioUnitSubType_HALOutput; @@ -97,7 +97,7 @@ Error AudioDriverCoreAudio::init() { AudioStreamBasicDescription strdesc; - zeromem(&strdesc, sizeof(strdesc)); + memset(&strdesc, 0, sizeof(strdesc)); UInt32 size = sizeof(strdesc); result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &strdesc, &size); ERR_FAIL_COND_V(result != noErr, FAILED); @@ -118,7 +118,7 @@ Error AudioDriverCoreAudio::init() { mix_rate = GLOBAL_GET("audio/driver/mix_rate"); - zeromem(&strdesc, sizeof(strdesc)); + memset(&strdesc, 0, sizeof(strdesc)); strdesc.mFormatID = kAudioFormatLinearPCM; strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; strdesc.mChannelsPerFrame = channels; @@ -148,7 +148,7 @@ Error AudioDriverCoreAudio::init() { print_verbose("CoreAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); AURenderCallbackStruct callback; - zeromem(&callback, sizeof(AURenderCallbackStruct)); + memset(&callback, 0, sizeof(AURenderCallbackStruct)); callback.inputProc = &AudioDriverCoreAudio::output_callback; callback.inputProcRefCon = this; result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); @@ -173,10 +173,10 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, if (!ad->active || !ad->try_lock()) { for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { AudioBuffer *abuf = &ioData->mBuffers[i]; - zeromem(abuf->mData, abuf->mDataByteSize); - }; + memset(abuf->mData, 0, abuf->mDataByteSize); + } return 0; - }; + } ad->start_counting_ticks(); @@ -195,14 +195,14 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, frames_left -= frames; out += frames * ad->channels; - }; - }; + } + } ad->stop_counting_ticks(); ad->unlock(); return 0; -}; +} OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, @@ -251,7 +251,7 @@ void AudioDriverCoreAudio::start() { active = true; } } -}; +} void AudioDriverCoreAudio::stop() { if (active) { @@ -266,19 +266,19 @@ void AudioDriverCoreAudio::stop() { int AudioDriverCoreAudio::get_mix_rate() const { return mix_rate; -}; +} AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); -}; +} void AudioDriverCoreAudio::lock() { mutex.lock(); -}; +} void AudioDriverCoreAudio::unlock() { mutex.unlock(); -}; +} bool AudioDriverCoreAudio::try_lock() { return mutex.try_lock() == OK; @@ -293,7 +293,7 @@ void AudioDriverCoreAudio::finish() { lock(); AURenderCallbackStruct callback; - zeromem(&callback, sizeof(AURenderCallbackStruct)); + memset(&callback, 0, sizeof(AURenderCallbackStruct)); result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); if (result != noErr) { ERR_PRINT("AudioUnitSetProperty failed"); @@ -337,7 +337,7 @@ void AudioDriverCoreAudio::finish() { Error AudioDriverCoreAudio::capture_init() { AudioComponentDescription desc; - zeromem(&desc, sizeof(desc)); + memset(&desc, 0, sizeof(desc)); desc.componentType = kAudioUnitType_Output; #ifdef OSX_ENABLED desc.componentSubType = kAudioUnitSubType_HALOutput; @@ -383,7 +383,7 @@ Error AudioDriverCoreAudio::capture_init() { #endif AudioStreamBasicDescription strdesc; - zeromem(&strdesc, sizeof(strdesc)); + memset(&strdesc, 0, sizeof(strdesc)); size = sizeof(strdesc); result = AudioUnitGetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, &size); ERR_FAIL_COND_V(result != noErr, FAILED); @@ -405,7 +405,7 @@ Error AudioDriverCoreAudio::capture_init() { mix_rate = GLOBAL_GET("audio/driver/mix_rate"); - zeromem(&strdesc, sizeof(strdesc)); + memset(&strdesc, 0, sizeof(strdesc)); strdesc.mFormatID = kAudioFormatLinearPCM; strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; strdesc.mChannelsPerFrame = capture_channels; @@ -419,7 +419,7 @@ Error AudioDriverCoreAudio::capture_init() { ERR_FAIL_COND_V(result != noErr, FAILED); AURenderCallbackStruct callback; - zeromem(&callback, sizeof(AURenderCallbackStruct)); + memset(&callback, 0, sizeof(AURenderCallbackStruct)); callback.inputProc = &AudioDriverCoreAudio::input_callback; callback.inputProcRefCon = this; result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callback, sizeof(callback)); @@ -436,7 +436,7 @@ void AudioDriverCoreAudio::capture_finish() { lock(); AURenderCallbackStruct callback; - zeromem(&callback, sizeof(AURenderCallbackStruct)); + memset(&callback, 0, sizeof(AURenderCallbackStruct)); OSStatus result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback)); if (result != noErr) { ERR_PRINT("AudioUnitSetProperty failed"); @@ -506,7 +506,8 @@ Array AudioDriverCoreAudio::_get_device_list(bool capture) { UInt32 size = 0; AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size); - AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size); + AudioDeviceID *audioDevices = (AudioDeviceID *)memalloc(size); + ERR_FAIL_NULL_V_MSG(audioDevices, list, "Out of memory."); AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, nullptr, &size, audioDevices); UInt32 deviceCount = size / sizeof(AudioDeviceID); @@ -515,14 +516,16 @@ Array AudioDriverCoreAudio::_get_device_list(bool capture) { prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size); - AudioBufferList *bufferList = (AudioBufferList *)malloc(size); + AudioBufferList *bufferList = (AudioBufferList *)memalloc(size); + ERR_FAIL_NULL_V_MSG(bufferList, list, "Out of memory."); AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, bufferList); UInt32 channelCount = 0; - for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) + for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) { channelCount += bufferList->mBuffers[j].mNumberChannels; + } - free(bufferList); + memfree(bufferList); if (channelCount >= 1) { CFStringRef cfname; @@ -534,17 +537,18 @@ Array AudioDriverCoreAudio::_get_device_list(bool capture) { CFIndex length = CFStringGetLength(cfname); CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; - char *buffer = (char *)malloc(maxSize); + char *buffer = (char *)memalloc(maxSize); + ERR_FAIL_NULL_V_MSG(buffer, list, "Out of memory."); if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { // Append the ID to the name in case we have devices with duplicate name - list.push_back(String(buffer) + " (" + itos(audioDevices[i]) + ")"); + list.push_back(String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")"); } - free(buffer); + memfree(buffer); } } - free(audioDevices); + memfree(audioDevices); return list; } @@ -561,7 +565,8 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { UInt32 size = 0; AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size); - AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size); + AudioDeviceID *audioDevices = (AudioDeviceID *)memalloc(size); + ERR_FAIL_NULL_MSG(audioDevices, "Out of memory."); AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, nullptr, &size, audioDevices); UInt32 deviceCount = size / sizeof(AudioDeviceID); @@ -570,14 +575,16 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size); - AudioBufferList *bufferList = (AudioBufferList *)malloc(size); + AudioBufferList *bufferList = (AudioBufferList *)memalloc(size); + ERR_FAIL_NULL_MSG(bufferList, "Out of memory."); AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, bufferList); UInt32 channelCount = 0; - for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) + for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) { channelCount += bufferList->mBuffers[j].mNumberChannels; + } - free(bufferList); + memfree(bufferList); if (channelCount >= 1) { CFStringRef cfname; @@ -589,20 +596,21 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { CFIndex length = CFStringGetLength(cfname); CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; - char *buffer = (char *)malloc(maxSize); + char *buffer = (char *)memalloc(maxSize); + ERR_FAIL_NULL_MSG(buffer, "Out of memory."); if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { - String name = String(buffer) + " (" + itos(audioDevices[i]) + ")"; + String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")"; if (name == device) { deviceId = audioDevices[i]; found = true; } } - free(buffer); + memfree(buffer); } } - free(audioDevices); + memfree(audioDevices); } if (!found) { diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index b31835760e..b19f133d89 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/coremidi/midi_driver_coremidi.cpp b/drivers/coremidi/midi_driver_coremidi.cpp index 87764d9b10..ecd10f900b 100644 --- a/drivers/coremidi/midi_driver_coremidi.cpp +++ b/drivers/coremidi/midi_driver_coremidi.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/coremidi/midi_driver_coremidi.h b/drivers/coremidi/midi_driver_coremidi.h index 41a7c760ac..be0a9f610e 100644 --- a/drivers/coremidi/midi_driver_coremidi.h +++ b/drivers/coremidi/midi_driver_coremidi.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h deleted file mode 100644 index a76520dcdb..0000000000 --- a/drivers/dummy/rasterizer_dummy.h +++ /dev/null @@ -1,774 +0,0 @@ -/*************************************************************************/ -/* rasterizer_dummy.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RASTERIZER_DUMMY_H -#define RASTERIZER_DUMMY_H - -#include "core/math/camera_matrix.h" -#include "core/templates/rid_owner.h" -#include "core/templates/self_list.h" -#include "scene/resources/mesh.h" -#include "servers/rendering/renderer_compositor.h" -#include "servers/rendering_server.h" - -class RasterizerSceneDummy : public RendererSceneRender { -public: - GeometryInstance *geometry_instance_create(RID p_base) override { return nullptr; } - void geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) override {} - void geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) override {} - void geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_material) override {} - void geometry_instance_set_mesh_instance(GeometryInstance *p_geometry_instance, RID p_mesh_instance) override {} - void geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) override {} - void geometry_instance_set_layer_mask(GeometryInstance *p_geometry_instance, uint32_t p_layer_mask) override {} - void geometry_instance_set_lod_bias(GeometryInstance *p_geometry_instance, float p_lod_bias) override {} - void geometry_instance_set_use_baked_light(GeometryInstance *p_geometry_instance, bool p_enable) override {} - void geometry_instance_set_use_dynamic_gi(GeometryInstance *p_geometry_instance, bool p_enable) override {} - void geometry_instance_set_use_lightmap(GeometryInstance *p_geometry_instance, RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override {} - void geometry_instance_set_lightmap_capture(GeometryInstance *p_geometry_instance, const Color *p_sh9) override {} - void geometry_instance_set_instance_shader_parameters_offset(GeometryInstance *p_geometry_instance, int32_t p_offset) override {} - void geometry_instance_set_cast_double_sided_shadows(GeometryInstance *p_geometry_instance, bool p_enable) override {} - - uint32_t geometry_instance_get_pair_mask() override { return 0; } - void geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) override {} - void geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override {} - void geometry_instance_pair_decal_instances(GeometryInstance *p_geometry_instance, const RID *p_decal_instances, uint32_t p_decal_instance_count) override {} - void geometry_instance_pair_gi_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_gi_probe_instances, uint32_t p_gi_probe_instance_count) override {} - - void geometry_instance_free(GeometryInstance *p_geometry_instance) override {} - - /* SHADOW ATLAS API */ - - RID shadow_atlas_create() override { return RID(); } - void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = false) override {} - void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override {} - bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override { return false; } - - void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = false) override {} - int get_directional_light_shadow_size(RID p_light_intance) override { return 0; } - void set_directional_shadow_count(int p_count) override {} - - /* SDFGI UPDATE */ - - void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} - int sdfgi_get_pending_region_count(RID p_render_buffers) const override { return 0; } - AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override { return AABB(); } - uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override { return 0; } - - /* SKY API */ - - RID sky_allocate() override { return RID(); } - void sky_initialize(RID p_rid) override {} - void sky_set_radiance_size(RID p_sky, int p_radiance_size) override {} - void sky_set_mode(RID p_sky, RS::SkyMode p_samples) override {} - void sky_set_material(RID p_sky, RID p_material) override {} - Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override { return Ref<Image>(); } - - /* ENVIRONMENT API */ - - RID environment_allocate() override { return RID(); } - void environment_initialize(RID p_rid) override {} - void environment_set_background(RID p_env, RS::EnvironmentBG p_bg) override {} - void environment_set_sky(RID p_env, RID p_sky) override {} - void environment_set_sky_custom_fov(RID p_env, float p_scale) override {} - void environment_set_sky_orientation(RID p_env, const Basis &p_orientation) override {} - void environment_set_bg_color(RID p_env, const Color &p_color) override {} - void environment_set_bg_energy(RID p_env, float p_energy) override {} - void environment_set_canvas_max_layer(RID p_env, int p_max_layer) override {} - void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG, const Color &p_ao_color = Color()) override {} - - void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) override {} - void environment_glow_set_use_bicubic_upscale(bool p_enable) override {} - void environment_glow_set_use_high_quality(bool p_enable) override {} - - void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) override {} - void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override {} - void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_power, float p_detail, float p_horizon, float p_sharpness, float p_light_affect, float p_ao_channel_affect) override {} - void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {} - - void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override {} - - void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override {} - void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override {} - void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override {} - - void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) override {} - - void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) override {} - - void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) override {} - void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount) override {} - void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override {} - void environment_set_volumetric_fog_filter_active(bool p_enable) override {} - - Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override { return Ref<Image>(); } - - bool is_environment(RID p_env) const override { return false; } - RS::EnvironmentBG environment_get_background(RID p_env) const override { return RS::ENV_BG_KEEP; } - int environment_get_canvas_max_layer(RID p_env) const override { return 0; } - - RID camera_effects_allocate() override { return RID(); } - void camera_effects_initialize(RID p_rid) override {} - void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override {} - void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override {} - - void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override {} - void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override {} - - void shadows_quality_set(RS::ShadowQuality p_quality) override {} - void directional_shadow_quality_set(RS::ShadowQuality p_quality) override {} - - RID light_instance_create(RID p_light) override { return RID(); } - void light_instance_set_transform(RID p_light_instance, const Transform &p_transform) override {} - void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override {} - void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override {} - void light_instance_mark_visible(RID p_light_instance) override {} - - RID reflection_atlas_create() override { return RID(); } - int reflection_atlas_get_size(RID p_ref_atlas) const override { return 0; } - void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override {} - - RID reflection_probe_instance_create(RID p_probe) override { return RID(); } - void reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform) override {} - void reflection_probe_release_atlas_index(RID p_instance) override {} - bool reflection_probe_instance_needs_redraw(RID p_instance) override { return false; } - bool reflection_probe_instance_has_reflection(RID p_instance) override { return false; } - bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override { return false; } - bool reflection_probe_instance_postprocess_step(RID p_instance) override { return true; } - - RID decal_instance_create(RID p_decal) override { return RID(); } - void decal_instance_set_transform(RID p_decal, const Transform &p_transform) override {} - - RID lightmap_instance_create(RID p_lightmap) override { return RID(); } - void lightmap_instance_set_transform(RID p_lightmap, const Transform &p_transform) override {} - - RID gi_probe_instance_create(RID p_gi_probe) override { return RID(); } - void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) override {} - bool gi_probe_needs_update(RID p_probe) const override { return false; } - void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects) override {} - - void gi_probe_set_quality(RS::GIProbeQuality) override {} - - void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) override {} - void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override {} - void render_particle_collider_heightfield(RID p_collider, const Transform &p_transform, const PagedArray<GeometryInstance *> &p_instances) override {} - - void set_scene_pass(uint64_t p_pass) override {} - void set_time(double p_time, double p_step) override {} - void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override {} - - RID render_buffers_create() override { return RID(); } - void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) override {} - void gi_set_use_half_resolution(bool p_enable) override {} - - void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override {} - bool screen_space_roughness_limiter_is_active() const override { return false; } - - void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override {} - void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override {} - - TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) override { return TypedArray<Image>(); } - - bool free(RID p_rid) override { return true; } - void update() override {} - void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override {} - - bool is_low_end() const override { return true; } - - RasterizerSceneDummy() {} - ~RasterizerSceneDummy() {} -}; - -class RasterizerStorageDummy : public RendererStorage { -public: - bool can_create_resources_async() const override { return false; } - - /* TEXTURE API */ - struct DummyTexture { - Ref<Image> image; - }; - mutable RID_PtrOwner<DummyTexture> texture_owner; - - RID texture_allocate() override { - DummyTexture *texture = memnew(DummyTexture); - ERR_FAIL_COND_V(!texture, RID()); - return texture_owner.make_rid(texture); - } - void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override { - DummyTexture *t = texture_owner.getornull(p_texture); - ERR_FAIL_COND(!t); - t->image = p_image->duplicate(); - } - - void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override {} - void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override {} - void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override {} - 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 {} - void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override {} - void texture_proxy_initialize(RID p_texture, RID p_base) override {} - void texture_proxy_update(RID p_proxy, RID p_base) override {} - - void texture_2d_placeholder_initialize(RID p_texture) override {} - void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override {} - void texture_3d_placeholder_initialize(RID p_texture) override {} - - Ref<Image> texture_2d_get(RID p_texture) const override { - DummyTexture *t = texture_owner.getornull(p_texture); - ERR_FAIL_COND_V(!t, Ref<Image>()); - return t->image; - } - - Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const override { return Ref<Image>(); } - Vector<Ref<Image>> texture_3d_get(RID p_texture) const override { return Vector<Ref<Image>>(); } - - void texture_replace(RID p_texture, RID p_by_texture) override {} - void texture_set_size_override(RID p_texture, int p_width, int p_height) override {} - - void texture_set_path(RID p_texture, const String &p_path) override {} - String texture_get_path(RID p_texture) const override { return String(); } - - void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override {} - void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override {} - void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) override {} - - void texture_debug_usage(List<RS::TextureInfo> *r_info) override {} - void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) override {} - Size2 texture_size_with_proxy(RID p_proxy) override { return Size2(); } - - void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} - void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} - - /* CANVAS TEXTURE API */ - - RID canvas_texture_allocate() override { return RID(); } - void canvas_texture_initialize(RID p_rid) override {} - void canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) override {} - void canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_base_color, float p_shininess) override {} - - void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override {} - void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override {} - - /* SHADER API */ - - RID shader_allocate() override { return RID(); } - void shader_initialize(RID p_rid) override {} - void shader_set_code(RID p_shader, const String &p_code) override {} - String shader_get_code(RID p_shader) const override { return ""; } - void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override {} - - void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture) override {} - RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const override { return RID(); } - Variant shader_get_param_default(RID p_material, const StringName &p_param) const override { return Variant(); } - - RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override { return RS::ShaderNativeSourceCode(); }; - - /* COMMON MATERIAL API */ - - RID material_allocate() override { return RID(); } - void material_initialize(RID p_rid) override {} - void material_set_render_priority(RID p_material, int priority) override {} - void material_set_shader(RID p_shader_material, RID p_shader) override {} - - void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) override {} - Variant material_get_param(RID p_material, const StringName &p_param) const override { return Variant(); } - - void material_set_next_pass(RID p_material, RID p_next_material) override {} - - bool material_is_animated(RID p_material) override { return false; } - bool material_casts_shadows(RID p_material) override { return false; } - void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override {} - void material_update_dependency(RID p_material, DependencyTracker *p_instance) override {} - - /* MESH API */ - - RID mesh_allocate() override { return RID(); } - void mesh_initialize(RID p_rid) override {} - void mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) override {} - bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override { return false; } - RID mesh_instance_create(RID p_base) override { return RID(); } - void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override {} - void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override {} - void mesh_instance_check_for_update(RID p_mesh_instance) override {} - void update_mesh_instances() override {} - void reflection_probe_set_lod_threshold(RID p_probe, float p_ratio) override {} - float reflection_probe_get_lod_threshold(RID p_probe) const override { return 0.0; } - - void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) override {} - - int mesh_get_blend_shape_count(RID p_mesh) const override { return 0; } - - void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) override {} - RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const override { return RS::BLEND_SHAPE_MODE_NORMALIZED; } - - void mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override {} - - void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override {} - RID mesh_surface_get_material(RID p_mesh, int p_surface) const override { return RID(); } - - RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override { return RS::SurfaceData(); } - int mesh_get_surface_count(RID p_mesh) const override { return 0; } - - void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override {} - AABB mesh_get_custom_aabb(RID p_mesh) const override { return AABB(); } - - AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override { return AABB(); } - void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override {} - void mesh_clear(RID p_mesh) override {} - - /* MULTIMESH API */ - - RID multimesh_allocate() override { return RID(); } - void multimesh_initialize(RID p_rid) override {} - 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 {} - int multimesh_get_instance_count(RID p_multimesh) const override { return 0; } - - void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {} - void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) override {} - void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override {} - void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override {} - void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override {} - - RID multimesh_get_mesh(RID p_multimesh) const override { return RID(); } - AABB multimesh_get_aabb(RID p_multimesh) const override { return AABB(); } - - Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform(); } - Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); } - Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); } - Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); } - void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override {} - Vector<float> multimesh_get_buffer(RID p_multimesh) const override { return Vector<float>(); } - - void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override {} - int multimesh_get_visible_instances(RID p_multimesh) const override { return 0; } - - /* IMMEDIATE API */ - - RID immediate_allocate() override { return RID(); } - void immediate_initialize(RID p_rid) override {} - void immediate_begin(RID p_immediate, RS::PrimitiveType p_rimitive, RID p_texture = RID()) override {} - void immediate_vertex(RID p_immediate, const Vector3 &p_vertex) override {} - void immediate_normal(RID p_immediate, const Vector3 &p_normal) override {} - void immediate_tangent(RID p_immediate, const Plane &p_tangent) override {} - void immediate_color(RID p_immediate, const Color &p_color) override {} - void immediate_uv(RID p_immediate, const Vector2 &tex_uv) override {} - void immediate_uv2(RID p_immediate, const Vector2 &tex_uv) override {} - void immediate_end(RID p_immediate) override {} - void immediate_clear(RID p_immediate) override {} - void immediate_set_material(RID p_immediate, RID p_material) override {} - RID immediate_get_material(RID p_immediate) const override { return RID(); } - AABB immediate_get_aabb(RID p_immediate) const override { return AABB(); } - - /* SKELETON API */ - - RID skeleton_allocate() override { return RID(); } - void skeleton_initialize(RID p_rid) override {} - void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override {} - void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override {} - int skeleton_get_bone_count(RID p_skeleton) const override { return 0; } - void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) override {} - Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override { return Transform(); } - void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) override {} - Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const override { return Transform2D(); } - - /* Light API */ - - RID directional_light_allocate() override { return RID(); } - void directional_light_initialize(RID p_rid) override {} - RID omni_light_allocate() override { return RID(); } - void omni_light_initialize(RID p_rid) override {} - RID spot_light_allocate() override { return RID(); } - void spot_light_initialize(RID p_rid) override {} - RID reflection_probe_allocate() override { return RID(); } - void reflection_probe_initialize(RID p_rid) override {} - - void light_set_color(RID p_light, const Color &p_color) override {} - void light_set_param(RID p_light, RS::LightParam p_param, float p_value) override {} - void light_set_shadow(RID p_light, bool p_enabled) override {} - void light_set_shadow_color(RID p_light, const Color &p_color) override {} - void light_set_projector(RID p_light, RID p_texture) override {} - void light_set_negative(RID p_light, bool p_enable) override {} - void light_set_cull_mask(RID p_light, uint32_t p_mask) override {} - void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override {} - void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override {} - void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {} - - void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override {} - - void light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) override {} - void light_directional_set_blend_splits(RID p_light, bool p_enable) override {} - bool light_directional_get_blend_splits(RID p_light) const override { return false; } - void light_directional_set_shadow_depth_range_mode(RID p_light, RS::LightDirectionalShadowDepthRangeMode p_range_mode) override {} - void light_directional_set_sky_only(RID p_light, bool p_sky_only) override {} - bool light_directional_is_sky_only(RID p_light) const override { return false; } - RS::LightDirectionalShadowDepthRangeMode light_directional_get_shadow_depth_range_mode(RID p_light) const override { return RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; } - - RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override { return RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; } - RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override { return RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; } - - bool light_has_shadow(RID p_light) const override { return false; } - - RS::LightType light_get_type(RID p_light) const override { return RS::LIGHT_OMNI; } - AABB light_get_aabb(RID p_light) const override { return AABB(); } - float light_get_param(RID p_light, RS::LightParam p_param) override { return 0.0; } - Color light_get_color(RID p_light) override { return Color(); } - RS::LightBakeMode light_get_bake_mode(RID p_light) override { return RS::LIGHT_BAKE_DISABLED; } - uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; } - uint64_t light_get_version(RID p_light) const override { return 0; } - - /* PROBE API */ - - void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override {} - void reflection_probe_set_intensity(RID p_probe, float p_intensity) override {} - void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override {} - void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override {} - void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) override {} - void reflection_probe_set_max_distance(RID p_probe, float p_distance) override {} - void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) override {} - void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) override {} - void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override {} - void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override {} - void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override {} - void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override {} - void reflection_probe_set_resolution(RID p_probe, int p_resolution) override {} - - AABB reflection_probe_get_aabb(RID p_probe) const override { return AABB(); } - RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override { return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE; } - uint32_t reflection_probe_get_cull_mask(RID p_probe) const override { return 0; } - Vector3 reflection_probe_get_extents(RID p_probe) const override { return Vector3(); } - Vector3 reflection_probe_get_origin_offset(RID p_probe) const override { return Vector3(); } - float reflection_probe_get_origin_max_distance(RID p_probe) const override { return 0.0; } - bool reflection_probe_renders_shadows(RID p_probe) const override { return false; } - - void base_update_dependency(RID p_base, DependencyTracker *p_instance) override {} - void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override {} - - /* DECAL API */ - - RID decal_allocate() override { return RID(); } - void decal_initialize(RID p_rid) override {} - void decal_set_extents(RID p_decal, const Vector3 &p_extents) override {} - void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) override {} - void decal_set_emission_energy(RID p_decal, float p_energy) override {} - void decal_set_albedo_mix(RID p_decal, float p_mix) override {} - void decal_set_modulate(RID p_decal, const Color &p_modulate) override {} - void decal_set_cull_mask(RID p_decal, uint32_t p_layers) override {} - void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) override {} - void decal_set_fade(RID p_decal, float p_above, float p_below) override {} - void decal_set_normal_fade(RID p_decal, float p_fade) override {} - - AABB decal_get_aabb(RID p_decal) const override { return AABB(); } - - /* GI PROBE API */ - - RID gi_probe_allocate() override { return RID(); } - void gi_probe_initialize(RID p_rid) override {} - void gi_probe_allocate_data(RID p_gi_probe, const Transform &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override {} - - AABB gi_probe_get_bounds(RID p_gi_probe) const override { return AABB(); } - Vector3i gi_probe_get_octree_size(RID p_gi_probe) const override { return Vector3i(); } - Vector<uint8_t> gi_probe_get_octree_cells(RID p_gi_probe) const override { return Vector<uint8_t>(); } - Vector<uint8_t> gi_probe_get_data_cells(RID p_gi_probe) const override { return Vector<uint8_t>(); } - Vector<uint8_t> gi_probe_get_distance_field(RID p_gi_probe) const override { return Vector<uint8_t>(); } - - Vector<int> gi_probe_get_level_counts(RID p_gi_probe) const override { return Vector<int>(); } - Transform gi_probe_get_to_cell_xform(RID p_gi_probe) const override { return Transform(); } - - void gi_probe_set_dynamic_range(RID p_gi_probe, float p_range) override {} - float gi_probe_get_dynamic_range(RID p_gi_probe) const override { return 0; } - - void gi_probe_set_propagation(RID p_gi_probe, float p_range) override {} - float gi_probe_get_propagation(RID p_gi_probe) const override { return 0; } - - void gi_probe_set_energy(RID p_gi_probe, float p_range) override {} - float gi_probe_get_energy(RID p_gi_probe) const override { return 0.0; } - - void gi_probe_set_ao(RID p_gi_probe, float p_ao) override {} - float gi_probe_get_ao(RID p_gi_probe) const override { return 0; } - - void gi_probe_set_ao_size(RID p_gi_probe, float p_strength) override {} - float gi_probe_get_ao_size(RID p_gi_probe) const override { return 0; } - - void gi_probe_set_bias(RID p_gi_probe, float p_range) override {} - float gi_probe_get_bias(RID p_gi_probe) const override { return 0.0; } - - void gi_probe_set_normal_bias(RID p_gi_probe, float p_range) override {} - float gi_probe_get_normal_bias(RID p_gi_probe) const override { return 0.0; } - - void gi_probe_set_interior(RID p_gi_probe, bool p_enable) override {} - bool gi_probe_is_interior(RID p_gi_probe) const override { return false; } - - void gi_probe_set_use_two_bounces(RID p_gi_probe, bool p_enable) override {} - bool gi_probe_is_using_two_bounces(RID p_gi_probe) const override { return false; } - - void gi_probe_set_anisotropy_strength(RID p_gi_probe, float p_strength) override {} - float gi_probe_get_anisotropy_strength(RID p_gi_probe) const override { return 0; } - - uint32_t gi_probe_get_version(RID p_gi_probe) override { return 0; } - - /* LIGHTMAP CAPTURE */ - RID lightmap_allocate() override { return RID(); } - void lightmap_initialize(RID p_rid) override {} - void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) override {} - void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override {} - void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override {} - void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override {} - PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override { return PackedVector3Array(); } - PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override { return PackedColorArray(); } - PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override { return PackedInt32Array(); } - PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const override { return PackedInt32Array(); } - AABB lightmap_get_aabb(RID p_lightmap) const override { return AABB(); } - void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) override {} - bool lightmap_is_interior(RID p_lightmap) const override { return false; } - void lightmap_set_probe_capture_update_speed(float p_speed) override {} - float lightmap_get_probe_capture_update_speed() const override { return 0; } - - /* PARTICLES */ - - RID particles_allocate() override { return RID(); } - void particles_initialize(RID p_rid) override {} - void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) override {} - void particles_set_emitting(RID p_particles, bool p_emitting) override {} - void particles_set_amount(RID p_particles, int p_amount) override {} - void particles_set_lifetime(RID p_particles, float p_lifetime) override {} - void particles_set_one_shot(RID p_particles, bool p_one_shot) override {} - void particles_set_pre_process_time(RID p_particles, float p_time) override {} - void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) override {} - void particles_set_randomness_ratio(RID p_particles, float p_ratio) override {} - void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override {} - void particles_set_speed_scale(RID p_particles, float p_scale) override {} - void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override {} - void particles_set_process_material(RID p_particles, RID p_material) override {} - void particles_set_fixed_fps(RID p_particles, int p_fps) override {} - void particles_set_fractional_delta(RID p_particles, bool p_enable) override {} - void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override {} - void particles_set_view_axis(RID p_particles, const Vector3 &p_axis) override {} - void particles_set_collision_base_size(RID p_particles, float p_size) override {} - void particles_restart(RID p_particles) override {} - - void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override {} - - void particles_set_draw_passes(RID p_particles, int p_count) override {} - void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) override {} - - void particles_request_process(RID p_particles) override {} - AABB particles_get_current_aabb(RID p_particles) override { return AABB(); } - AABB particles_get_aabb(RID p_particles) const override { return AABB(); } - - void particles_set_emission_transform(RID p_particles, const Transform &p_transform) override {} - - bool particles_get_emitting(RID p_particles) override { return false; } - int particles_get_draw_passes(RID p_particles) const override { return 0; } - RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const override { return RID(); } - - void particles_add_collision(RID p_particles, RID p_instance) override {} - void particles_remove_collision(RID p_particles, RID p_instance) override {} - - void update_particles() override {} - - /* PARTICLES COLLISION */ - - RID particles_collision_allocate() override { return RID(); } - void particles_collision_initialize(RID p_rid) override {} - void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) override {} - void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) override {} - void particles_collision_set_sphere_radius(RID p_particles_collision, float p_radius) override {} - void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) override {} - void particles_collision_set_attractor_strength(RID p_particles_collision, float p_strength) override {} - void particles_collision_set_attractor_directionality(RID p_particles_collision, float p_directionality) override {} - void particles_collision_set_attractor_attenuation(RID p_particles_collision, float p_curve) override {} - void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) override {} - void particles_collision_height_field_update(RID p_particles_collision) override {} - void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override {} - AABB particles_collision_get_aabb(RID p_particles_collision) const override { return AABB(); } - bool particles_collision_is_heightfield(RID p_particles_collision) const override { return false; } - RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override { return RID(); } - - RID particles_collision_instance_create(RID p_collision) override { return RID(); }; - void particles_collision_instance_set_transform(RID p_collision_instance, const Transform &p_transform) override{}; - void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override{}; - - /* GLOBAL VARIABLES */ - - void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) override {} - void global_variable_remove(const StringName &p_name) override {} - Vector<StringName> global_variable_get_list() const override { return Vector<StringName>(); } - - void global_variable_set(const StringName &p_name, const Variant &p_value) override {} - void global_variable_set_override(const StringName &p_name, const Variant &p_value) override {} - Variant global_variable_get(const StringName &p_name) const override { return Variant(); } - RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const override { return RS::GLOBAL_VAR_TYPE_MAX; } - - void global_variables_load_settings(bool p_load_textures = true) override {} - void global_variables_clear() override {} - - int32_t global_variables_instance_allocate(RID p_instance) override { return 0; } - void global_variables_instance_free(RID p_instance) override {} - void global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) override {} - - bool particles_is_inactive(RID p_particles) const override { return false; } - - /* RENDER TARGET */ - - RID render_target_create() override { return RID(); } - void render_target_set_position(RID p_render_target, int p_x, int p_y) override {} - void render_target_set_size(RID p_render_target, int p_width, int p_height) override {} - RID render_target_get_texture(RID p_render_target) override { return RID(); } - void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override {} - void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) override {} - bool render_target_was_used(RID p_render_target) override { return false; } - void render_target_set_as_unused(RID p_render_target) override {} - - void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override {} - bool render_target_is_clear_requested(RID p_render_target) override { return false; } - Color render_target_get_clear_request_color(RID p_render_target) override { return Color(); } - void render_target_disable_clear_request(RID p_render_target) override {} - void render_target_do_clear_request(RID p_render_target) override {} - - void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override {} - Rect2i render_target_get_sdf_rect(RID p_render_target) const override { return Rect2i(); } - - RS::InstanceType get_base_type(RID p_rid) const override { return RS::INSTANCE_NONE; } - bool free(RID p_rid) override { - if (texture_owner.owns(p_rid)) { - // delete the texture - DummyTexture *texture = texture_owner.getornull(p_rid); - texture_owner.free(p_rid); - memdelete(texture); - } - return true; - } - - bool has_os_feature(const String &p_feature) const override { return false; } - - void update_dirty_resources() override {} - - void set_debug_generate_wireframes(bool p_generate) override {} - - void render_info_begin_capture() override {} - void render_info_end_capture() override {} - int get_captured_render_info(RS::RenderInfo p_info) override { return 0; } - - int get_render_info(RS::RenderInfo p_info) override { return 0; } - String get_video_adapter_name() const override { return String(); } - String get_video_adapter_vendor() const override { return String(); } - - static RendererStorage *base_singleton; - - void capture_timestamps_begin() override {} - void capture_timestamp(const String &p_name) override {} - uint32_t get_captured_timestamps_count() const override { return 0; } - uint64_t get_captured_timestamps_frame() const override { return 0; } - uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override { return 0; } - uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override { return 0; } - String get_captured_timestamp_name(uint32_t p_index) const override { return String(); } - - RasterizerStorageDummy() {} - ~RasterizerStorageDummy() {} -}; - -class RasterizerCanvasDummy : public RendererCanvasRender { -public: - PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override { return 0; } - void free_polygon(PolygonID p_polygon) override {} - - 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) override {} - void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override {} - - RID light_create() override { return RID(); } - void light_set_texture(RID p_rid, RID p_texture) override {} - void light_set_use_shadow(RID p_rid, bool p_enable) override {} - void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override {} - void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override {} - - void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override {} - RID occluder_polygon_create() override { return RID(); } - void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) override {} - void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) override {} - void set_shadow_texture_size(int p_size) override {} - - void draw_window_margins(int *p_margins, RID *p_margin_textures) override {} - - bool free(RID p_rid) override { return true; } - void update() override {} - - RasterizerCanvasDummy() {} - ~RasterizerCanvasDummy() {} -}; - -class RasterizerDummy : public RendererCompositor { -private: - uint64_t frame = 1; - float delta = 0; - -protected: - RasterizerCanvasDummy canvas; - RasterizerStorageDummy storage; - RasterizerSceneDummy scene; - -public: - RendererStorage *get_storage() override { return &storage; } - RendererCanvasRender *get_canvas() override { return &canvas; } - RendererSceneRender *get_scene() override { return &scene; } - - void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) override {} - - void initialize() override {} - void begin_frame(double frame_step) override { - frame++; - delta = frame_step; - } - - void prepare_for_blitting_render_targets() override {} - void blit_render_targets_to_screen(int p_screen, const BlitToScreen *p_render_targets, int p_amount) override {} - - void end_frame(bool p_swap_buffers) override { - if (p_swap_buffers) { - DisplayServer::get_singleton()->swap_buffers(); - } - } - - void finalize() override {} - - static RendererCompositor *_create_current() { - return memnew(RasterizerDummy); - } - - static void make_current() { - _create_func = _create_current; - } - - bool is_low_end() const override { return true; } - uint64_t get_frame_number() const override { return frame; } - float get_frame_delta_time() const override { return delta; } - - RasterizerDummy() {} - ~RasterizerDummy() {} -}; - -#endif // RASTERIZER_DUMMY_H diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub new file mode 100644 index 0000000000..ddeec6f4c6 --- /dev/null +++ b/drivers/gl_context/SCsub @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +Import("env") + +if env["platform"] in ["haiku", "osx", "windows", "linuxbsd"]: + # Thirdparty source files + thirdparty_dir = "#thirdparty/glad/" + thirdparty_sources = [ + "glad.c", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + + env.Prepend(CPPPATH=[thirdparty_dir]) + + env.Append(CPPDEFINES=["GLAD_ENABLED"]) + env.Append(CPPDEFINES=["GLES_OVER_GL"]) + + env_thirdparty = env.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) + +# Godot source files +env.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/gles3/SCsub b/drivers/gles3/SCsub new file mode 100644 index 0000000000..fcb05a988d --- /dev/null +++ b/drivers/gles3/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.drivers_sources, "*.cpp") + +SConscript("shaders/SCsub") +SConscript("storage/SCsub") diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp new file mode 100644 index 0000000000..b47d2e08f1 --- /dev/null +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -0,0 +1,1476 @@ +/*************************************************************************/ +/* rasterizer_canvas_gles3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_canvas_gles3.h" + +#ifdef GLES3_ENABLED + +#include "core/os/os.h" +#include "rasterizer_scene_gles3.h" +#include "rasterizer_storage_gles3.h" + +#include "core/config/project_settings.h" +#include "servers/rendering/rendering_server_default.h" +#include "storage/canvas_texture_storage.h" +#include "storage/config.h" + +#ifndef GLES_OVER_GL +#define glClearDepth glClearDepthf +#endif + +//static const GLenum gl_primitive[] = { +// GL_POINTS, +// GL_LINES, +// GL_LINE_STRIP, +// GL_LINE_LOOP, +// GL_TRIANGLES, +// GL_TRIANGLE_STRIP, +// GL_TRIANGLE_FAN +//}; + +void RasterizerCanvasGLES3::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) { + p_mat4[0] = p_transform.elements[0][0]; + p_mat4[1] = p_transform.elements[0][1]; + p_mat4[2] = 0; + p_mat4[3] = 0; + p_mat4[4] = p_transform.elements[1][0]; + p_mat4[5] = p_transform.elements[1][1]; + p_mat4[6] = 0; + p_mat4[7] = 0; + p_mat4[8] = 0; + p_mat4[9] = 0; + p_mat4[10] = 1; + p_mat4[11] = 0; + p_mat4[12] = p_transform.elements[2][0]; + p_mat4[13] = p_transform.elements[2][1]; + p_mat4[14] = 0; + p_mat4[15] = 1; +} + +void RasterizerCanvasGLES3::_update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4) { + p_mat2x4[0] = p_transform.elements[0][0]; + p_mat2x4[1] = p_transform.elements[1][0]; + p_mat2x4[2] = 0; + p_mat2x4[3] = p_transform.elements[2][0]; + + p_mat2x4[4] = p_transform.elements[0][1]; + p_mat2x4[5] = p_transform.elements[1][1]; + p_mat2x4[6] = 0; + p_mat2x4[7] = p_transform.elements[2][1]; +} + +void RasterizerCanvasGLES3::_update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3) { + p_mat2x3[0] = p_transform.elements[0][0]; + p_mat2x3[1] = p_transform.elements[0][1]; + p_mat2x3[2] = p_transform.elements[1][0]; + p_mat2x3[3] = p_transform.elements[1][1]; + p_mat2x3[4] = p_transform.elements[2][0]; + p_mat2x3[5] = p_transform.elements[2][1]; +} + +void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4) { + p_mat4[0] = p_transform.basis.elements[0][0]; + p_mat4[1] = p_transform.basis.elements[1][0]; + p_mat4[2] = p_transform.basis.elements[2][0]; + p_mat4[3] = 0; + p_mat4[4] = p_transform.basis.elements[0][1]; + p_mat4[5] = p_transform.basis.elements[1][1]; + p_mat4[6] = p_transform.basis.elements[2][1]; + p_mat4[7] = 0; + p_mat4[8] = p_transform.basis.elements[0][2]; + p_mat4[9] = p_transform.basis.elements[1][2]; + p_mat4[10] = p_transform.basis.elements[2][2]; + p_mat4[11] = 0; + p_mat4[12] = p_transform.origin.x; + p_mat4[13] = p_transform.origin.y; + p_mat4[14] = p_transform.origin.z; + p_mat4[15] = 1; +} + +void RasterizerCanvasGLES3::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) { + storage->frame.current_rt = nullptr; + + storage->_set_current_render_target(p_to_render_target); + + Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse(); + + // TODO: Setup Directional Lights + + // TODO: Setup lights + + { + //update canvas state uniform buffer + StateBuffer state_buffer; + + Size2i ssize = storage->render_target_get_size(p_to_render_target); + + Transform3D screen_transform; + screen_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f); + screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f)); + _update_transform_to_mat4(screen_transform, state_buffer.screen_transform); + _update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform); + + Transform2D normal_transform = p_canvas_transform; + normal_transform.elements[0].normalize(); + normal_transform.elements[1].normalize(); + normal_transform.elements[2] = Vector2(); + _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform); + + state_buffer.canvas_modulate[0] = p_modulate.r; + state_buffer.canvas_modulate[1] = p_modulate.g; + state_buffer.canvas_modulate[2] = p_modulate.b; + state_buffer.canvas_modulate[3] = p_modulate.a; + + Size2 render_target_size = storage->render_target_get_size(p_to_render_target); + state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; + state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y; + + state_buffer.time = storage->frame.time; + state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel; + + state_buffer.directional_light_count = 0; //directional_light_count; + + Vector2 canvas_scale = p_canvas_transform.get_scale(); + + state_buffer.sdf_to_screen[0] = render_target_size.width / canvas_scale.x; + state_buffer.sdf_to_screen[1] = render_target_size.height / canvas_scale.y; + + state_buffer.screen_to_sdf[0] = 1.0 / state_buffer.sdf_to_screen[0]; + state_buffer.screen_to_sdf[1] = 1.0 / state_buffer.sdf_to_screen[1]; + + Rect2 sdf_rect = storage->render_target_get_sdf_rect(p_to_render_target); + Rect2 sdf_tex_rect(sdf_rect.position / canvas_scale, sdf_rect.size / canvas_scale); + + state_buffer.sdf_to_tex[0] = 1.0 / sdf_tex_rect.size.width; + state_buffer.sdf_to_tex[1] = 1.0 / sdf_tex_rect.size.height; + state_buffer.sdf_to_tex[2] = -sdf_tex_rect.position.x / sdf_tex_rect.size.width; + state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height; + + //print_line("w: " + itos(ssize.width) + " s: " + rtos(canvas_scale)); + state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_state_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + + { + state.default_filter = p_default_filter; + state.default_repeat = p_default_repeat; + } + + state.current_tex = RID(); + state.current_tex_ptr = nullptr; + state.current_normal = RID(); + state.current_specular = RID(); + state.canvas_texscreen_used = false; + + r_sdf_used = false; + int item_count = 0; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + Item *ci = p_item_list; + while (ci) { + // just add all items for now + items[item_count++] = ci; + + if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) { + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); + //then reset + item_count = 0; + } + + ci = ci->next; + } +} + +void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer) { + Item *current_clip = nullptr; + + Transform2D canvas_transform_inverse = p_canvas_transform_inverse; + + RID framebuffer; + Vector<Color> clear_colors; + + canvas_begin(); + + RID prev_material; + uint32_t index = 0; + + for (int i = 0; i < p_item_count; i++) { + Item *ci = items[i]; + + RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; + RasterizerStorageGLES3::Material *material_ptr = storage->material_owner.get_or_null(material); + + if (material.is_null() && ci->canvas_group != nullptr) { + material = default_canvas_group_material; + } + + if (material != prev_material) { + RasterizerStorageGLES3::Shader *shader_ptr = nullptr; + + if (material_ptr) { + shader_ptr = material_ptr->shader; + + if (shader_ptr && shader_ptr->mode != RS::SHADER_CANVAS_ITEM) { + shader_ptr = nullptr; // not a canvas item shader, don't use. + } + } + + if (shader_ptr) { + if (true) { //check that shader has changed + if (shader_ptr->canvas_item.uses_time) { + RenderingServerDefault::redraw_request(); + } + //state.canvas_shader.version_bind_shader(shader_ptr->version, CanvasShaderGLES3::MODE_QUAD); + state.current_shader_version = shader_ptr->version; + } + + int tc = material_ptr->textures.size(); + Pair<StringName, RID> *textures = material_ptr->textures.ptrw(); + + ShaderCompiler::GeneratedCode::Texture *texture_uniforms = shader_ptr->texture_uniforms.ptrw(); + + for (int ti = 0; ti < tc; i++) { + glActiveTexture(GL_TEXTURE0 + ti); + + GLES3::Texture *t = texture_storage->get_texture(textures[ti].second); + + if (!t) { + switch (texture_uniforms[i].hint) { + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { + glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_ANISOTROPY: { + glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { + glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); + } break; + default: { + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + } break; + } + + continue; + } + + //Set texture filter and repeat texture_uniforms[i].filter texture_uniforms[i].repeat + + if (t->redraw_if_visible) { + RenderingServerDefault::redraw_request(); + } + + t = t->get_ptr(); + +#ifdef TOOLS_ENABLED + if (t->detect_normal && texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL) { + t->detect_normal(t->detect_normal_ud); + } +#endif + if (t->render_target) { + t->render_target->used_in_frame = true; + } + + glBindTexture(t->target, t->tex_id); + } + + } else { + //state.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); + state.current_shader_version = state.canvas_shader_default_version; + } + prev_material = material; + } + + _render_item(p_to_render_target, ci, canvas_transform_inverse, current_clip, p_lights, index); + } + // Render last command + state.end_batch = true; + _render_batch(index); + + canvas_end(); +} + +void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, uint32_t &r_index) { + RS::CanvasItemTextureFilter current_filter = state.default_filter; + RS::CanvasItemTextureRepeat current_repeat = state.default_repeat; + + if (p_item->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) { + current_filter = p_item->texture_filter; + } + + if (p_item->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) { + current_repeat = p_item->texture_repeat; + } + + Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; + Transform2D draw_transform; // Used by transform command + + Color base_color = p_item->final_modulate; + + uint32_t base_flags = 0; + + RID last_texture; + Size2 texpixel_size; + + bool skipping = false; + + const Item::Command *c = p_item->commands; + while (c) { + if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { + c = c->next; + continue; + } + + _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); + + for (int i = 0; i < 4; i++) { + state.instance_data_array[r_index].modulation[i] = 0.0; + state.instance_data_array[r_index].ninepatch_margins[i] = 0.0; + state.instance_data_array[r_index].src_rect[i] = 0.0; + state.instance_data_array[r_index].dst_rect[i] = 0.0; + state.instance_data_array[r_index].lights[i] = uint32_t(0); + } + state.instance_data_array[r_index].flags = base_flags; + 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].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 sanity, keep canvastexture binding config + + switch (c->type) { + case Item::Command::TYPE_RECT: { + const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); + + if (rect->flags & CANVAS_RECT_TILE) { + current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; + } + + if (rect->texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_RECT) { + state.end_batch = true; + _render_batch(r_index); + + state.current_primitive_points = 0; + state.current_command = Item::Command::TYPE_RECT; + } + _bind_canvas_texture(rect->texture, current_filter, current_repeat, r_index, last_texture, texpixel_size); + state.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_QUAD); + + Rect2 src_rect; + Rect2 dst_rect; + + if (rect->texture != RID()) { + src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); + dst_rect = Rect2(rect->rect.position, rect->rect.size); + + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } + + if (rect->flags & CANVAS_RECT_FLIP_H) { + src_rect.size.x *= -1; + } + + if (rect->flags & CANVAS_RECT_FLIP_V) { + src_rect.size.y *= -1; + } + + if (rect->flags & CANVAS_RECT_TRANSPOSE) { + dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform + } + + if (rect->flags & CANVAS_RECT_CLIP_UV) { + state.instance_data_array[r_index].flags |= FLAGS_CLIP_RECT_UV; + } + + } else { + dst_rect = Rect2(rect->rect.position, rect->rect.size); + + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } + + src_rect = Rect2(0, 0, 1, 1); + } + + if (rect->flags & CANVAS_RECT_MSDF) { + state.instance_data_array[r_index].flags |= 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. + } + + state.instance_data_array[r_index].modulation[0] = rect->modulate.r * base_color.r; + state.instance_data_array[r_index].modulation[1] = rect->modulate.g * base_color.g; + state.instance_data_array[r_index].modulation[2] = rect->modulate.b * base_color.b; + state.instance_data_array[r_index].modulation[3] = rect->modulate.a * base_color.a; + + state.instance_data_array[r_index].src_rect[0] = src_rect.position.x; + state.instance_data_array[r_index].src_rect[1] = src_rect.position.y; + state.instance_data_array[r_index].src_rect[2] = src_rect.size.width; + state.instance_data_array[r_index].src_rect[3] = src_rect.size.height; + + state.instance_data_array[r_index].dst_rect[0] = dst_rect.position.x; + state.instance_data_array[r_index].dst_rect[1] = dst_rect.position.y; + 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; + //_render_batch(r_index); + r_index++; + if (r_index >= state.max_instances_per_batch - 1) { + //r_index--; + state.end_batch = true; + _render_batch(r_index); + } + } break; + + case Item::Command::TYPE_NINEPATCH: { + /* + const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); + + //bind pipeline + { + RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_NINEPATCH].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + } + + //bind textures + + _bind_canvas_texture(p_draw_list, np->texture, current_filter, current_repeat, index, last_texture, texpixel_size); + + Rect2 src_rect; + Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y); + + if (np->texture == RID()) { + texpixel_size = Size2(1, 1); + src_rect = Rect2(0, 0, 1, 1); + + } else { + if (np->source != Rect2()) { + src_rect = Rect2(np->source.position.x * texpixel_size.width, np->source.position.y * texpixel_size.height, np->source.size.x * texpixel_size.width, np->source.size.y * texpixel_size.height); + state.instance_data_array[r_index].color_texture_pixel_size[0] = 1.0 / np->source.size.width; + state.instance_data_array[r_index].color_texture_pixel_size[1] = 1.0 / np->source.size.height; + + } else { + src_rect = Rect2(0, 0, 1, 1); + } + } + + state.instance_data_array[r_index].modulation[0] = np->color.r * base_color.r; + state.instance_data_array[r_index].modulation[1] = np->color.g * base_color.g; + state.instance_data_array[r_index].modulation[2] = np->color.b * base_color.b; + state.instance_data_array[r_index].modulation[3] = np->color.a * base_color.a; + + state.instance_data_array[r_index].src_rect[0] = src_rect.position.x; + state.instance_data_array[r_index].src_rect[1] = src_rect.position.y; + state.instance_data_array[r_index].src_rect[2] = src_rect.size.width; + state.instance_data_array[r_index].src_rect[3] = src_rect.size.height; + + state.instance_data_array[r_index].dst_rect[0] = dst_rect.position.x; + state.instance_data_array[r_index].dst_rect[1] = dst_rect.position.y; + 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; + + if (np->draw_center) { + state.instance_data_array[r_index].flags |= FLAGS_NINEPACH_DRAW_CENTER; + } + + state.instance_data_array[r_index].ninepatch_margins[0] = np->margin[SIDE_LEFT]; + state.instance_data_array[r_index].ninepatch_margins[1] = np->margin[SIDE_TOP]; + state.instance_data_array[r_index].ninepatch_margins[2] = np->margin[SIDE_RIGHT]; + state.instance_data_array[r_index].ninepatch_margins[3] = np->margin[SIDE_BOTTOM]; + + RD::get_singleton()->draw_list_set_state.instance_data_array[r_index](p_draw_list, &state.instance_data_array[r_index], sizeof(PushConstant)); + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); + RD::get_singleton()->draw_list_draw(p_draw_list, true); + + // Restore if overridden. + state.instance_data_array[r_index].color_texture_pixel_size[0] = texpixel_size.x; + state.instance_data_array[r_index].color_texture_pixel_size[1] = texpixel_size.y; +*/ + } break; + + case Item::Command::TYPE_POLYGON: { + const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); + + PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); + ERR_CONTINUE(!pb); + + if (polygon->texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_POLYGON) { + state.end_batch = true; + _render_batch(r_index); + + state.current_primitive_points = 0; + state.current_command = Item::Command::TYPE_POLYGON; + } + _bind_canvas_texture(polygon->texture, current_filter, current_repeat, r_index, last_texture, texpixel_size); + state.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES); + + state.current_primitive = polygon->primitive; + state.instance_data_array[r_index].modulation[0] = base_color.r; + state.instance_data_array[r_index].modulation[1] = base_color.g; + state.instance_data_array[r_index].modulation[2] = base_color.b; + state.instance_data_array[r_index].modulation[3] = base_color.a; + + for (int j = 0; j < 4; j++) { + state.instance_data_array[r_index].src_rect[j] = 0; + state.instance_data_array[r_index].dst_rect[j] = 0; + state.instance_data_array[r_index].ninepatch_margins[j] = 0; + } + + // If the previous operation is not done yet, allocated a new buffer + GLint syncStatus; + glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + if (syncStatus == GL_UNSIGNALED) { + _allocate_instance_data_buffer(); + } else { + glDeleteSync(state.fences[state.current_buffer]); + } + + glBindBufferBase(GL_UNIFORM_BUFFER, 3, state.canvas_instance_data_buffers[state.current_buffer]); +#ifdef JAVASCRIPT_ENABLED + //WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead + glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData), &state.instance_data_array[0], GL_DYNAMIC_DRAW); +#else + void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(ubo, &state.instance_data_array[0], sizeof(InstanceData)); + glUnmapBuffer(GL_UNIFORM_BUFFER); +#endif + glBindVertexArray(pb->vertex_array); + + static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + + if (pb->index_buffer != 0) { + glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr); + } else { + glDrawArrays(prim[polygon->primitive], 0, pb->count); + } + glBindVertexArray(0); + state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); + } break; + + case Item::Command::TYPE_PRIMITIVE: { + const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); + + if (last_texture != default_canvas_texture || state.current_primitive_points != primitive->point_count || state.current_command != Item::Command::TYPE_PRIMITIVE) { + state.end_batch = true; + _render_batch(r_index); + state.current_primitive_points = primitive->point_count; + state.current_command = Item::Command::TYPE_PRIMITIVE; + } + _bind_canvas_texture(RID(), current_filter, current_repeat, r_index, last_texture, texpixel_size); + state.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_PRIMITIVE); + + for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) { + state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j].x; + state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j].y; + state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j].x; + state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j].y; + Color col = primitive->colors[j] * base_color; + state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } + r_index++; + if (primitive->point_count == 4) { + // Reset base data + _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); + 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].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 sanity, keep canvastexture binding config + + for (uint32_t j = 0; j < 3; j++) { + //second half of triangle + state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + 1].x; + state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + 1].y; + state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + 1].x; + state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + 1].y; + Color col = primitive->colors[j + 1] * base_color; + state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } + r_index++; + } + if (r_index >= state.max_instances_per_batch - 1) { + //r_index--; + state.end_batch = true; + _render_batch(r_index); + } + } break; + + case Item::Command::TYPE_MESH: + case Item::Command::TYPE_MULTIMESH: + case Item::Command::TYPE_PARTICLES: { + /* + RID mesh; + RID mesh_instance; + RID texture; + Color modulate(1, 1, 1, 1); + int instance_count = 1; + + if (c->type == Item::Command::TYPE_MESH) { + const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c); + mesh = m->mesh; + mesh_instance = m->mesh_instance; + texture = m->texture; + modulate = m->modulate; + _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, state.instance_data_array[r_index].world); + } else if (c->type == Item::Command::TYPE_MULTIMESH) { + const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c); + RID multimesh = mm->multimesh; + mesh = storage->multimesh_get_mesh(multimesh); + texture = mm->texture; + + if (storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { + break; + } + + instance_count = storage->multimesh_get_instances_to_draw(multimesh); + + if (instance_count == 0) { + break; + } + + state.instance_data_array[r_index].flags |= 1; //multimesh, trails disabled + if (storage->multimesh_uses_colors(multimesh)) { + state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS; + } + if (storage->multimesh_uses_custom_data(multimesh)) { + state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; + } + } + + // TODO: implement particles here + + if (mesh.is_null()) { + break; + } + + if (texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_PRIMITIVE) { + state.end_batch = true; + _render_batch(r_index); + state.current_primitive_points = 0; + state.current_command = c->type; + } + + _bind_canvas_texture(texture, current_filter, current_repeat, r_index, last_texture, texpixel_size); + + uint32_t surf_count = storage->mesh_get_surface_count(mesh); + + state.instance_data_array[r_index].modulation[0] = base_color.r * modulate.r; + state.instance_data_array[r_index].modulation[1] = base_color.g * modulate.g; + state.instance_data_array[r_index].modulation[2] = base_color.b * modulate.b; + state.instance_data_array[r_index].modulation[3] = base_color.a * modulate.a; + + for (int j = 0; j < 4; j++) { + state.instance_data_array[r_index].src_rect[j] = 0; + state.instance_data_array[r_index].dst_rect[j] = 0; + state.instance_data_array[r_index].ninepatch_margins[j] = 0; + } + + for (uint32_t j = 0; j < surf_count; j++) { + RS::SurfaceData *surface = storage->mesh_get_surface(mesh, j); + + RS::PrimitiveType primitive = storage->mesh_surface_get_primitive(surface); + ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); + + glBindVertexArray(surface->vertex_array); + static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + + // Draw directly, no need to batch + } + */ + } break; + case Item::Command::TYPE_TRANSFORM: { + const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c); + draw_transform = transform->xform; + } break; + + case Item::Command::TYPE_CLIP_IGNORE: { + /* + const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); + if (current_clip) { + if (ci->ignore != reclip) { + if (ci->ignore) { + RD::get_singleton()->draw_list_disable_scissor(p_draw_list); + reclip = true; + } else { + RD::get_singleton()->draw_list_enable_scissor(p_draw_list, current_clip->final_clip_rect); + reclip = false; + } + } + } + */ + } break; + case Item::Command::TYPE_ANIMATION_SLICE: { + /* + const Item::CommandAnimationSlice *as = static_cast<const Item::CommandAnimationSlice *>(c); + double current_time = RendererCompositorRD::singleton->get_total_time(); + double local_time = Math::fposmod(current_time - as->offset, as->animation_length); + skipping = !(local_time >= as->slice_begin && local_time < as->slice_end); + + RenderingServerDefault::redraw_request(); // animation visible means redraw request + */ + } break; + } + + c = c->next; + } +} + +void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) { + if (state.end_batch && r_index > 0) { + // If the previous operation is not done yet, allocate a new buffer + GLint syncStatus; + glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + if (syncStatus == GL_UNSIGNALED) { + _allocate_instance_data_buffer(); + } else { + glDeleteSync(state.fences[state.current_buffer]); + } + + glBindBufferBase(GL_UNIFORM_BUFFER, 3, state.canvas_instance_data_buffers[state.current_buffer]); +#ifdef JAVASCRIPT_ENABLED + //WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead + glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * r_index, state.instance_data_array, GL_DYNAMIC_DRAW); +#else + void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * r_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * r_index); + glUnmapBuffer(GL_UNIFORM_BUFFER); +#endif + glBindVertexArray(data.canvas_quad_array); + if (state.current_primitive_points == 0) { + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, r_index); + } else { + static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES }; + glDrawArraysInstanced(prim[state.current_primitive_points], 0, state.current_primitive_points, r_index); + } + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); + state.end_batch = false; + //copy the new data into the base of the batch + for (int i = 0; i < 4; i++) { + state.instance_data_array[0].modulation[i] = state.instance_data_array[r_index].modulation[i]; + state.instance_data_array[0].ninepatch_margins[i] = state.instance_data_array[r_index].ninepatch_margins[i]; + state.instance_data_array[0].src_rect[i] = state.instance_data_array[r_index].src_rect[i]; + state.instance_data_array[0].dst_rect[i] = state.instance_data_array[r_index].dst_rect[i]; + state.instance_data_array[0].lights[i] = state.instance_data_array[r_index].lights[i]; + } + state.instance_data_array[0].flags = state.instance_data_array[r_index].flags; + state.instance_data_array[0].color_texture_pixel_size[0] = state.instance_data_array[r_index].color_texture_pixel_size[0]; + state.instance_data_array[0].color_texture_pixel_size[1] = state.instance_data_array[r_index].color_texture_pixel_size[1]; + + state.instance_data_array[0].pad[0] = state.instance_data_array[r_index].pad[0]; + state.instance_data_array[0].pad[1] = state.instance_data_array[r_index].pad[1]; + for (int i = 0; i < 6; i++) { + state.instance_data_array[0].world[i] = state.instance_data_array[r_index].world[i]; + } + + r_index = 0; + } +} + +// TODO maybe dont use +void RasterizerCanvasGLES3::_end_batch(uint32_t &r_index) { + for (int i = 0; i < 4; i++) { + state.instance_data_array[r_index].modulation[i] = 0.0; + state.instance_data_array[r_index].ninepatch_margins[i] = 0.0; + state.instance_data_array[r_index].src_rect[i] = 0.0; + state.instance_data_array[r_index].dst_rect[i] = 0.0; + } + state.instance_data_array[r_index].flags = uint32_t(0); + 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] = uint32_t(0); + state.instance_data_array[r_index].lights[1] = uint32_t(0); + state.instance_data_array[r_index].lights[2] = uint32_t(0); + state.instance_data_array[r_index].lights[3] = uint32_t(0); +} + +RID RasterizerCanvasGLES3::light_create() { + return RID(); +} + +void RasterizerCanvasGLES3::light_set_texture(RID p_rid, RID p_texture) { +} + +void RasterizerCanvasGLES3::light_set_use_shadow(RID p_rid, bool 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_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) { +} + +void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) { +} + +RID RasterizerCanvasGLES3::occluder_polygon_create() { + return RID(); +} + +void RasterizerCanvasGLES3::occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) { +} + +void RasterizerCanvasGLES3::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) { +} + +void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) { +} + +bool RasterizerCanvasGLES3::free(RID p_rid) { + return true; +} + +void RasterizerCanvasGLES3::update() { +} + +void RasterizerCanvasGLES3::canvas_begin() { + state.using_transparent_rt = false; + + if (storage->frame.current_rt) { + storage->bind_framebuffer(storage->frame.current_rt->fbo); + state.using_transparent_rt = storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]; + } + + if (storage->frame.current_rt && storage->frame.current_rt->clear_requested) { + const Color &col = storage->frame.current_rt->clear_color; + glClearColor(col.r, col.g, col.b, col.a); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + storage->frame.current_rt->clear_requested = false; + } + + reset_canvas(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); +} + +void RasterizerCanvasGLES3::canvas_end() { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, RID &r_last_texture, Size2 &r_texpixel_size) { + if (p_texture == RID()) { + p_texture = default_canvas_texture; + } + + if (r_last_texture == p_texture) { + return; //nothing to do, its the same + } + + state.end_batch = true; + _render_batch(r_index); + + GLES3::CanvasTexture *ct = nullptr; + + GLES3::Texture *t = texture_storage->get_texture(p_texture); + + if (t) { + //regular texture + if (!t->canvas_texture) { + t->canvas_texture = memnew(GLES3::CanvasTexture); + t->canvas_texture->diffuse = p_texture; + } + + ct = t->canvas_texture; + } else { + ct = GLES3::CanvasTextureStorage::get_singleton()->get_canvas_texture(p_texture); + } + + if (!ct) { + // Invalid Texture RID. + _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index, r_last_texture, r_texpixel_size); + return; + } + + RS::CanvasItemTextureFilter filter = ct->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? ct->texture_filter : p_base_filter; + ERR_FAIL_COND(filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT); + + RS::CanvasItemTextureRepeat repeat = ct->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? ct->texture_repeat : p_base_repeat; + ERR_FAIL_COND(repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + + GLES3::Texture *texture = texture_storage->get_texture(ct->diffuse); + + if (!texture) { + state.current_tex = RID(); + state.current_tex_ptr = nullptr; + ct->size_cache = Size2i(1, 1); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + + } else { + texture = texture->get_ptr(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->tex_id); + + state.current_tex = ct->diffuse; + state.current_tex_ptr = texture; + ct->size_cache = Size2i(texture->width, texture->height); + + texture->GLSetFilter(GL_TEXTURE_2D, filter); + texture->GLSetRepeat(GL_TEXTURE_2D, repeat); + } + + GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map); + + if (!normal_map) { + state.current_normal = RID(); + ct->use_normal_cache = false; + glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); + + } else { + normal_map = normal_map->get_ptr(); + + glActiveTexture(GL_TEXTURE0 + storage->config->max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_2D, normal_map->tex_id); + state.current_normal = ct->normal_map; + ct->use_normal_cache = true; + texture->GLSetFilter(GL_TEXTURE_2D, filter); + texture->GLSetRepeat(GL_TEXTURE_2D, repeat); + } + + GLES3::Texture *specular_map = texture_storage->get_texture(ct->specular); + + if (!specular_map) { + state.current_specular = RID(); + ct->use_specular_cache = false; + glActiveTexture(GL_TEXTURE0 + storage->config->max_texture_image_units - 7); + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + + } else { + specular_map = specular_map->get_ptr(); + + glActiveTexture(GL_TEXTURE0 + storage->config->max_texture_image_units - 7); + glBindTexture(GL_TEXTURE_2D, specular_map->tex_id); + state.current_specular = ct->specular; + ct->use_specular_cache = true; + texture->GLSetFilter(GL_TEXTURE_2D, filter); + texture->GLSetRepeat(GL_TEXTURE_2D, repeat); + } + + if (ct->use_specular_cache) { + state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; + } else { + state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED; + } + + if (ct->use_normal_cache) { + state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; + } else { + state.instance_data_array[r_index].flags &= ~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)); + + r_texpixel_size.x = 1.0 / float(ct->size_cache.x); + r_texpixel_size.y = 1.0 / float(ct->size_cache.y); + + state.instance_data_array[r_index].color_texture_pixel_size[0] = r_texpixel_size.x; + state.instance_data_array[r_index].color_texture_pixel_size[1] = r_texpixel_size.y; + + r_last_texture = p_texture; +} + +void RasterizerCanvasGLES3::_set_uniforms() { +} + +void RasterizerCanvasGLES3::reset_canvas() { + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + + // Default to Mix. + glBlendEquation(GL_FUNC_ADD); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +void RasterizerCanvasGLES3::canvas_debug_viewport_shadows(Light *p_lights_with_shadow) { +} + +void RasterizerCanvasGLES3::canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache) { +} + +void RasterizerCanvasGLES3::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { +} + +RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, const Vector<int> &p_bones, const Vector<float> &p_weights) { + // We interleave the vertex data into one big VBO to improve cache coherence + uint32_t vertex_count = p_points.size(); + uint32_t stride = 2; + if ((uint32_t)p_colors.size() == vertex_count) { + stride += 4; + } + if ((uint32_t)p_uvs.size() == vertex_count) { + stride += 2; + } + if ((uint32_t)p_bones.size() == vertex_count * 4 && (uint32_t)p_weights.size() == vertex_count * 4) { + stride += 4; + } + + PolygonBuffers pb; + glGenBuffers(1, &pb.vertex_buffer); + glGenVertexArrays(1, &pb.vertex_array); + glBindVertexArray(pb.vertex_array); + pb.count = vertex_count; + pb.index_buffer = 0; + + uint32_t buffer_size = stride * p_points.size(); + + Vector<uint8_t> polygon_buffer; + polygon_buffer.resize(buffer_size * sizeof(float)); + { + glBindBuffer(GL_ARRAY_BUFFER, pb.vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, stride * vertex_count * sizeof(float), nullptr, GL_STATIC_DRAW); // TODO may not be necessary + const uint8_t *r = polygon_buffer.ptr(); + float *fptr = (float *)r; + uint32_t *uptr = (uint32_t *)r; + uint32_t base_offset = 0; + { + // Always uses vertex positions + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), nullptr); + const Vector2 *points_ptr = p_points.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = points_ptr[i].x; + fptr[base_offset + i * stride + 1] = points_ptr[i].y; + } + + base_offset += 2; + } + + // Next add colors + if (p_colors.size() == 1) { + glDisableVertexAttribArray(RS::ARRAY_COLOR); + Color m = p_colors[0]; + glVertexAttrib4f(RS::ARRAY_COLOR, m.r, m.g, m.b, m.a); + } else if ((uint32_t)p_colors.size() == vertex_count) { + glEnableVertexAttribArray(RS::ARRAY_COLOR); + glVertexAttribPointer(RS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float))); + + const Color *color_ptr = p_colors.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = color_ptr[i].r; + fptr[base_offset + i * stride + 1] = color_ptr[i].g; + fptr[base_offset + i * stride + 2] = color_ptr[i].b; + fptr[base_offset + i * stride + 3] = color_ptr[i].a; + } + base_offset += 4; + } else { + glDisableVertexAttribArray(RS::ARRAY_COLOR); + glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0); + } + + if ((uint32_t)p_uvs.size() == vertex_count) { + glEnableVertexAttribArray(RS::ARRAY_TEX_UV); + glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float))); + + const Vector2 *uv_ptr = p_uvs.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = uv_ptr[i].x; + fptr[base_offset + i * stride + 1] = uv_ptr[i].y; + } + + base_offset += 2; + } else { + glDisableVertexAttribArray(RS::ARRAY_TEX_UV); + } + + if ((uint32_t)p_indices.size() == vertex_count * 4 && (uint32_t)p_weights.size() == vertex_count * 4) { + glEnableVertexAttribArray(RS::ARRAY_BONES); + glVertexAttribPointer(RS::ARRAY_BONES, 4, GL_UNSIGNED_INT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float))); + + const int *bone_ptr = p_bones.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + uint16_t *bone16w = (uint16_t *)&uptr[base_offset + i * stride]; + + bone16w[0] = bone_ptr[i * 4 + 0]; + bone16w[1] = bone_ptr[i * 4 + 1]; + bone16w[2] = bone_ptr[i * 4 + 2]; + bone16w[3] = bone_ptr[i * 4 + 3]; + } + + base_offset += 2; + } else { + glDisableVertexAttribArray(RS::ARRAY_BONES); + } + + if ((uint32_t)p_weights.size() == vertex_count * 4) { + glEnableVertexAttribArray(RS::ARRAY_WEIGHTS); + glVertexAttribPointer(RS::ARRAY_WEIGHTS, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float))); + + const float *weight_ptr = p_weights.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + uint16_t *weight16w = (uint16_t *)&uptr[base_offset + i * stride]; + + weight16w[0] = CLAMP(weight_ptr[i * 4 + 0] * 65535, 0, 65535); + weight16w[1] = CLAMP(weight_ptr[i * 4 + 1] * 65535, 0, 65535); + weight16w[2] = CLAMP(weight_ptr[i * 4 + 2] * 65535, 0, 65535); + weight16w[3] = CLAMP(weight_ptr[i * 4 + 3] * 65535, 0, 65535); + } + + base_offset += 2; + } else { + glDisableVertexAttribArray(RS::ARRAY_WEIGHTS); + } + + ERR_FAIL_COND_V(base_offset != stride, 0); + glBufferData(GL_ARRAY_BUFFER, vertex_count * stride * sizeof(float), polygon_buffer.ptr(), GL_STATIC_DRAW); + } + + if (p_indices.size()) { + //create indices, as indices were requested + Vector<uint8_t> index_buffer; + index_buffer.resize(p_indices.size() * sizeof(int32_t)); + { + uint8_t *w = index_buffer.ptrw(); + memcpy(w, p_indices.ptr(), sizeof(int32_t) * p_indices.size()); + } + glGenBuffers(1, &pb.index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pb.index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, nullptr, GL_STATIC_DRAW); // TODO may not be necessary + glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, index_buffer.ptr(), GL_STATIC_DRAW); + pb.count = p_indices.size(); + } + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + PolygonID id = polygon_buffers.last_id++; + + polygon_buffers.polygons[id] = pb; + + return id; +} +void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) { + PolygonBuffers *pb_ptr = polygon_buffers.polygons.getptr(p_polygon); + ERR_FAIL_COND(!pb_ptr); + + PolygonBuffers &pb = *pb_ptr; + + if (pb.index_buffer != 0) { + glDeleteBuffers(1, &pb.index_buffer); + } + + glDeleteVertexArrays(1, &pb.vertex_array); + glDeleteBuffers(1, &pb.vertex_buffer); + + polygon_buffers.polygons.erase(p_polygon); +} + +// Creates a new uniform buffer and uses it right away +// This expands the instance buffer continually +// In theory allocations can reach as high as number_of_draw_calls * 3 frames +// because OpenGL can start rendering subsequent frames before finishing the current one +void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { + GLuint new_buffer; + glGenBuffers(1, &new_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, new_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW); + state.current_buffer = (state.current_buffer + 1); + state.canvas_instance_data_buffers.insert(state.current_buffer, new_buffer); + state.fences.insert(state.current_buffer, GLsync()); + state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size(); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +void RasterizerCanvasGLES3::initialize() { + // !BAS! shouldn't we be obtaining storage here as well? + canvas_texture_storage = GLES3::CanvasTextureStorage::get_singleton(); + texture_storage = GLES3::TextureStorage::get_singleton(); + + // quad buffer + { + glGenBuffers(1, &data.canvas_quad_vertices); + glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); + + const float qv[8] = { + 0, 0, + 0, 1, + 1, 1, + 1, 0 + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, qv, GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glGenVertexArrays(1, &data.canvas_quad_array); + glBindVertexArray(data.canvas_quad_array); + glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr); + glEnableVertexAttribArray(0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } + + { + //particle quad buffers + + glGenBuffers(1, &data.particle_quad_vertices); + glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices); + { + //quad of size 1, with pivot on the center for particles, then regular UVS. Color is general plus fetched from particle + const float qv[16] = { + -0.5, -0.5, + 0.0, 0.0, + -0.5, 0.5, + 0.0, 1.0, + 0.5, 0.5, + 1.0, 1.0, + 0.5, -0.5, + 1.0, 0.0 + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + glGenVertexArrays(1, &data.particle_quad_array); + glBindVertexArray(data.particle_quad_array); + glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr); + glEnableVertexAttribArray(RS::ARRAY_TEX_UV); + glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(8)); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } + + // ninepatch buffers + { + // array buffer + glGenBuffers(1, &data.ninepatch_vertices); + glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices); + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * (16 + 16) * 2, nullptr, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // element buffer + glGenBuffers(1, &data.ninepatch_elements); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ninepatch_elements); + +#define _EIDX(y, x) (y * 4 + x) + uint8_t elems[3 * 2 * 9] = { + // first row + + _EIDX(0, 0), _EIDX(0, 1), _EIDX(1, 1), + _EIDX(1, 1), _EIDX(1, 0), _EIDX(0, 0), + + _EIDX(0, 1), _EIDX(0, 2), _EIDX(1, 2), + _EIDX(1, 2), _EIDX(1, 1), _EIDX(0, 1), + + _EIDX(0, 2), _EIDX(0, 3), _EIDX(1, 3), + _EIDX(1, 3), _EIDX(1, 2), _EIDX(0, 2), + + // second row + + _EIDX(1, 0), _EIDX(1, 1), _EIDX(2, 1), + _EIDX(2, 1), _EIDX(2, 0), _EIDX(1, 0), + + // the center one would be here, but we'll put it at the end + // so it's easier to disable the center and be able to use + // one draw call for both + + _EIDX(1, 2), _EIDX(1, 3), _EIDX(2, 3), + _EIDX(2, 3), _EIDX(2, 2), _EIDX(1, 2), + + // third row + + _EIDX(2, 0), _EIDX(2, 1), _EIDX(3, 1), + _EIDX(3, 1), _EIDX(3, 0), _EIDX(2, 0), + + _EIDX(2, 1), _EIDX(2, 2), _EIDX(3, 2), + _EIDX(3, 2), _EIDX(3, 1), _EIDX(2, 1), + + _EIDX(2, 2), _EIDX(2, 3), _EIDX(3, 3), + _EIDX(3, 3), _EIDX(3, 2), _EIDX(2, 2), + + // center field + + _EIDX(1, 1), _EIDX(1, 2), _EIDX(2, 2), + _EIDX(2, 2), _EIDX(2, 1), _EIDX(1, 1) + }; +#undef _EIDX + + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elems), elems, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + //state.canvas_shadow_shader.init(); + + int uniform_max_size; + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &uniform_max_size); + if (uniform_max_size < 65536) { + state.max_lights_per_render = 64; + state.max_instances_per_batch = 128; + } else { + state.max_lights_per_render = 256; + state.max_instances_per_batch = 512; + } + + // Reserve 64 Uniform Buffers for instance data + state.canvas_instance_data_buffers.resize(64); + state.fences.resize(64); + glGenBuffers(64, state.canvas_instance_data_buffers.ptr()); + for (int i = 0; i < 64; i++) { + glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_instance_data_buffers[i]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW); + } + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + state.instance_data_array = memnew_arr(InstanceData, state.max_instances_per_batch); + + glGenBuffers(1, &state.canvas_state_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_state_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + String global_defines; + global_defines += "#define MAX_GLOBAL_VARIABLES 256\n"; // TODO: this is arbitrary for now + global_defines += "#define MAX_LIGHTS " + itos(state.max_instances_per_batch) + "\n"; + global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(state.max_instances_per_batch) + "\n"; + + state.canvas_shader.initialize(global_defines); + state.canvas_shader_default_version = state.canvas_shader.version_create(); + state.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); + + //state.canvas_shader.set_conditional(CanvasOldShaderGLES3::USE_RGBA_SHADOWS, storage->config->use_rgba_2d_shadows); + + //state.canvas_shader.bind(); + + //state.lens_shader.init(); + + //state.canvas_shader.set_conditional(CanvasOldShaderGLES3::USE_PIXEL_SNAP, GLOBAL_DEF("rendering/quality/2d/use_pixel_snap", false)); + + { + default_canvas_group_shader = storage->shader_allocate(); + storage->shader_initialize(default_canvas_group_shader); + + storage->shader_set_code(default_canvas_group_shader, R"( +// Default CanvasGroup shader. + +shader_type canvas_item; + +void fragment() { + vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); + + if (c.a > 0.0001) { + c.rgb /= c.a; + } + + COLOR *= c; +} +)"); + default_canvas_group_material = storage->material_allocate(); + storage->material_initialize(default_canvas_group_material); + + storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); + } + + default_canvas_texture = canvas_texture_storage->canvas_texture_allocate(); + canvas_texture_storage->canvas_texture_initialize(default_canvas_texture); + + state.using_light = nullptr; + state.using_transparent_rt = false; + state.using_skeleton = false; + state.current_shader_version = state.canvas_shader_default_version; +} + +RasterizerCanvasGLES3::RasterizerCanvasGLES3() { +} +RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { + state.canvas_shader.version_free(state.canvas_shader_default_version); + storage->free(default_canvas_group_material); + storage->free(default_canvas_group_shader); + canvas_texture_storage->canvas_texture_free(default_canvas_texture); +} + +void RasterizerCanvasGLES3::finalize() { + glDeleteBuffers(1, &data.canvas_quad_vertices); + glDeleteVertexArrays(1, &data.canvas_quad_array); + + glDeleteBuffers(1, &data.canvas_quad_vertices); + glDeleteVertexArrays(1, &data.canvas_quad_array); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h new file mode 100644 index 0000000000..1f57c2b5ea --- /dev/null +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -0,0 +1,285 @@ +/*************************************************************************/ +/* rasterizer_canvas_gles3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_CANVAS_OPENGL_H +#define RASTERIZER_CANVAS_OPENGL_H + +#ifdef GLES3_ENABLED + +#include "rasterizer_scene_gles3.h" +#include "rasterizer_storage_gles3.h" +#include "servers/rendering/renderer_canvas_render.h" +#include "servers/rendering/renderer_compositor.h" +#include "storage/canvas_texture_storage.h" +#include "storage/texture_storage.h" + +#include "shaders/canvas.glsl.gen.h" + +class RasterizerSceneGLES3; + +class RasterizerCanvasGLES3 : public RendererCanvasRender { + _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); + _FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3); + + _FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4); + _FORCE_INLINE_ void _update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4); + + enum { + BASE_UNIFORM_BUFFER_OBJECT = 0, + MATERIAL_UNIFORM_BUFFER_OBJECT = 1, + TRANSFORMS_UNIFORM_BUFFER_OBJECT = 2, + CANVAS_TEXTURE_UNIFORM_BUFFER_OBJECT = 3, + }; + + enum { + + FLAGS_INSTANCING_MASK = 0x7F, + FLAGS_INSTANCING_HAS_COLORS = (1 << 7), + FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8), + + FLAGS_CLIP_RECT_UV = (1 << 9), + FLAGS_TRANSPOSE_RECT = (1 << 10), + + FLAGS_NINEPACH_DRAW_CENTER = (1 << 12), + FLAGS_USING_PARTICLES = (1 << 13), + + FLAGS_USE_SKELETON = (1 << 15), + FLAGS_NINEPATCH_H_MODE_SHIFT = 16, + FLAGS_NINEPATCH_V_MODE_SHIFT = 18, + FLAGS_LIGHT_COUNT_SHIFT = 20, + + FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26), + FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27), + + FLAGS_USE_MSDF = (1 << 28), + }; + + enum { + LIGHT_FLAGS_TEXTURE_MASK = 0xFFFF, + LIGHT_FLAGS_BLEND_SHIFT = 16, + LIGHT_FLAGS_BLEND_MASK = (3 << 16), + LIGHT_FLAGS_BLEND_MODE_ADD = (0 << 16), + LIGHT_FLAGS_BLEND_MODE_SUB = (1 << 16), + LIGHT_FLAGS_BLEND_MODE_MIX = (2 << 16), + LIGHT_FLAGS_BLEND_MODE_MASK = (3 << 16), + LIGHT_FLAGS_HAS_SHADOW = (1 << 20), + LIGHT_FLAGS_FILTER_SHIFT = 22 + + }; + + enum { + MAX_RENDER_ITEMS = 256 * 1024, + MAX_LIGHT_TEXTURES = 1024, + MAX_LIGHTS_PER_ITEM = 16, + DEFAULT_MAX_LIGHTS_PER_RENDER = 256, + }; + +public: + struct StateBuffer { + float canvas_transform[16]; + float screen_transform[16]; + float canvas_normal_transform[16]; + float canvas_modulate[4]; + + float screen_pixel_size[2]; + float time; + uint32_t use_pixel_snap; + + float sdf_to_tex[4]; + float sdf_to_screen[2]; + float screen_to_sdf[2]; + + uint32_t directional_light_count; + float tex_to_sdf; + uint32_t pad1; + uint32_t pad2; + }; + + struct InstanceData { + float world[6]; + float color_texture_pixel_size[2]; + union { + //rect + struct { + float modulation[4]; + union { + float msdf[4]; + float ninepatch_margins[4]; + }; + float dst_rect[4]; + float src_rect[4]; + float pad[2]; + }; + //primitive + struct { + float points[6]; // vec2 points[3] + float uvs[6]; // vec2 points[3] + uint32_t colors[6]; // colors encoded as half + }; + }; + uint32_t flags; + uint32_t specular_shininess; + uint32_t lights[4]; + }; + + struct Data { + GLuint canvas_quad_vertices; + GLuint canvas_quad_array; + + GLuint particle_quad_vertices; + GLuint particle_quad_array; + + GLuint ninepatch_vertices; + GLuint ninepatch_elements; + } data; + + struct State { + GLuint canvas_state_buffer; + LocalVector<GLuint> canvas_instance_data_buffers; + LocalVector<GLsync> fences; + uint32_t current_buffer = 0; + + InstanceData *instance_data_array; + bool canvas_texscreen_used; + CanvasShaderGLES3 canvas_shader; + RID canvas_shader_current_version; + RID canvas_shader_default_version; + //CanvasShadowShaderGLES3 canvas_shadow_shader; + //LensDistortedShaderGLES3 lens_shader; + + bool using_texture_rect; + + bool using_ninepatch; + bool using_skeleton; + + Transform2D skeleton_transform; + Transform2D skeleton_transform_inverse; + Size2i skeleton_texture_size; + + RID current_tex = RID(); + RID current_normal = RID(); + RID current_specular = RID(); + GLES3::Texture *current_tex_ptr; + RID current_shader_version = RID(); + RS::PrimitiveType current_primitive = RS::PRIMITIVE_MAX; + uint32_t current_primitive_points = 0; + Item::Command::Type current_command = Item::Command::TYPE_RECT; + + bool end_batch = false; + + Transform3D vp; + Light *using_light; + bool using_shadow; + bool using_transparent_rt; + + // FROM RD Renderer + + uint32_t max_lights_per_render; + uint32_t max_lights_per_item; + uint32_t max_instances_per_batch; + + RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT; + RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT; + } state; + + Item *items[MAX_RENDER_ITEMS]; + + RID default_canvas_texture; + RID default_canvas_group_material; + RID default_canvas_group_shader; + + typedef void Texture; + + RasterizerSceneGLES3 *scene_render; + + GLES3::CanvasTextureStorage *canvas_texture_storage; + GLES3::TextureStorage *texture_storage; + RasterizerStorageGLES3 *storage; + + void _set_uniforms(); + + void canvas_begin(); + void canvas_end(); + + //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); + + virtual void reset_canvas(); + virtual void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache); + + virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override; + + RID light_create() override; + void light_set_texture(RID p_rid, RID p_texture) override; + void light_set_use_shadow(RID p_rid, bool p_enable) override; + void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override; + void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override; + + void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override; + RID occluder_polygon_create() override; + void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) override; + void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) override; + void set_shadow_texture_size(int p_size) override; + + bool free(RID p_rid) override; + void update() override; + + void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, RID &r_last_texture, Size2 &r_texpixel_size); + + struct PolygonBuffers { + GLuint vertex_buffer; + GLuint vertex_array; + GLuint index_buffer; + int count; + }; + + struct { + HashMap<PolygonID, PolygonBuffers> polygons; + PolygonID last_id; + } polygon_buffers; + + RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override; + void free_polygon(PolygonID p_polygon) override; + + 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) override; + void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false); + void _render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, uint32_t &r_index); + void _render_batch(uint32_t &p_max_index); + void _end_batch(uint32_t &p_max_index); + void _allocate_instance_data_buffer(); + + void initialize(); + void finalize(); + RasterizerCanvasGLES3(); + ~RasterizerCanvasGLES3(); +}; + +#endif // GLES3_ENABLED +#endif // RASTERIZER_CANVAS_OPENGL_H diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp new file mode 100644 index 0000000000..faadb2a4ed --- /dev/null +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -0,0 +1,363 @@ +/*************************************************************************/ +/* rasterizer_gles3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_gles3.h" + +#ifdef GLES3_ENABLED + +#include "core/config/project_settings.h" +#include "core/os/os.h" + +#define _EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define _EXT_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define _EXT_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define _EXT_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define _EXT_DEBUG_SOURCE_API_ARB 0x8246 +#define _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define _EXT_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define _EXT_DEBUG_SOURCE_OTHER_ARB 0x824B +#define _EXT_DEBUG_TYPE_ERROR_ARB 0x824C +#define _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define _EXT_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define _EXT_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define _EXT_DEBUG_TYPE_OTHER_ARB 0x8251 +#define _EXT_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define _EXT_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define _EXT_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define _EXT_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define _EXT_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148 +#define _EXT_DEBUG_OUTPUT 0x92E0 + +#ifndef GLAPIENTRY +#if defined(WINDOWS_ENABLED) && !defined(UWP_ENABLED) +#define GLAPIENTRY APIENTRY +#else +#define GLAPIENTRY +#endif +#endif + +#if !defined(IPHONE_ENABLED) && !defined(JAVASCRIPT_ENABLED) +// We include EGL below to get debug callback on GLES2 platforms, +// but EGL is not available on iOS. +#define CAN_DEBUG +#endif + +#if !defined(GLES_OVER_GL) && defined(CAN_DEBUG) +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> +#include <GLES3/gl3platform.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif + +#if defined(MINGW_ENABLED) || defined(_MSC_VER) +#define strcpy strcpy_s +#endif + +void RasterizerGLES3::begin_frame(double frame_step) { + frame++; + delta = frame_step; + + time_total += frame_step; + + double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs"); + time_total = Math::fmod(time_total, time_roll_over); + + storage.frame.time = time_total; + storage.frame.count++; + storage.frame.delta = frame_step; + + storage.update_dirty_resources(); + + storage.info.render_final = storage.info.render; + storage.info.render.reset(); + + //scene->iteration(); +} + +void RasterizerGLES3::end_frame(bool p_swap_buffers) { + // if (OS::get_singleton()->is_layered_allowed()) { + // if (!OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { + //clear alpha + // glColorMask(false, false, false, true); + // glClearColor(0.5, 0, 0, 1); + // glClear(GL_COLOR_BUFFER_BIT); + // glColorMask(true, true, true, true); + // } + // } + + // glClearColor(1, 0, 0, 1); + // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + if (p_swap_buffers) { + DisplayServer::get_singleton()->swap_buffers(); + } else { + glFinish(); + } +} + +#ifdef CAN_DEBUG +static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) { + if (type == _EXT_DEBUG_TYPE_OTHER_ARB) { + return; + } + + if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB) { + return; //these are ultimately annoying, so removing for now + } + + char debSource[256], debType[256], debSev[256]; + + if (source == _EXT_DEBUG_SOURCE_API_ARB) { + strcpy(debSource, "OpenGL"); + } else if (source == _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB) { + strcpy(debSource, "Windows"); + } else if (source == _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB) { + strcpy(debSource, "Shader Compiler"); + } else if (source == _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB) { + strcpy(debSource, "Third Party"); + } else if (source == _EXT_DEBUG_SOURCE_APPLICATION_ARB) { + strcpy(debSource, "Application"); + } else if (source == _EXT_DEBUG_SOURCE_OTHER_ARB) { + strcpy(debSource, "Other"); + } + + if (type == _EXT_DEBUG_TYPE_ERROR_ARB) { + strcpy(debType, "Error"); + } else if (type == _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB) { + strcpy(debType, "Deprecated behavior"); + } else if (type == _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB) { + strcpy(debType, "Undefined behavior"); + } else if (type == _EXT_DEBUG_TYPE_PORTABILITY_ARB) { + strcpy(debType, "Portability"); + } else if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB) { + strcpy(debType, "Performance"); + } else if (type == _EXT_DEBUG_TYPE_OTHER_ARB) { + strcpy(debType, "Other"); + } + + if (severity == _EXT_DEBUG_SEVERITY_HIGH_ARB) { + strcpy(debSev, "High"); + } else if (severity == _EXT_DEBUG_SEVERITY_MEDIUM_ARB) { + strcpy(debSev, "Medium"); + } else if (severity == _EXT_DEBUG_SEVERITY_LOW_ARB) { + strcpy(debSev, "Low"); + } + + String output = String() + "GL ERROR: Source: " + debSource + "\tType: " + debType + "\tID: " + itos(id) + "\tSeverity: " + debSev + "\tMessage: " + message; + + ERR_PRINT(output); +} +#endif + +typedef void (*DEBUGPROCARB)(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const char *message, + const void *userParam); + +typedef void (*DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam); + +void RasterizerGLES3::initialize() { + print_verbose("Using OpenGL video driver"); + + texture_storage.set_main_thread_id(Thread::get_caller_id()); + +#ifdef GLAD_ENABLED + if (!gladLoadGL()) { + ERR_PRINT("Error initializing GLAD"); + return; + } +#endif + +#ifdef GLAD_ENABLED + if (OS::get_singleton()->is_stdout_verbose()) { + if (GLAD_GL_ARB_debug_output) { + glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + glDebugMessageCallbackARB(_gl_debug_print, nullptr); + glEnable(_EXT_DEBUG_OUTPUT); + } else { + print_line("OpenGL debugging not supported!"); + } + } +#endif // GLAD_ENABLED + + // For debugging +#ifdef CAN_DEBUG +#ifdef GLES_OVER_GL + if (OS::get_singleton()->is_stdout_verbose() && GLAD_GL_ARB_debug_output) { + glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_ERROR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE); + glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE); + glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE); + glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PORTABILITY_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE); + glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PERFORMANCE_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE); + glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_OTHER_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE); + // glDebugMessageInsertARB( + // GL_DEBUG_SOURCE_API_ARB, + // GL_DEBUG_TYPE_OTHER_ARB, 1, + // GL_DEBUG_SEVERITY_HIGH_ARB, 5, "hello"); + } +#else + if (OS::get_singleton()->is_stdout_verbose()) { + DebugMessageCallbackARB callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallback"); + if (!callback) { + callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallbackKHR"); + } + + if (callback) { + print_line("godot: ENABLING GL DEBUG"); + glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + callback(_gl_debug_print, NULL); + glEnable(_EXT_DEBUG_OUTPUT); + } + } +#endif // GLES_OVER_GL +#endif // CAN_DEBUG + + print_line("OpenGL Renderer: " + RS::get_singleton()->get_video_adapter_name()); + storage.initialize(); + canvas.initialize(); + // scene.initialize(); + + // make sure the OS knows to only access the renderer from the main thread + OS::get_singleton()->set_render_main_thread_mode(OS::RENDER_MAIN_THREAD_ONLY); +} + +RasterizerGLES3::RasterizerGLES3() { + canvas.storage = &storage; + canvas.scene_render = &scene; + storage.canvas = &canvas; + //scene.storage = &storage; + storage.scene = &scene; +} + +void RasterizerGLES3::prepare_for_blitting_render_targets() { +} + +void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect) { + ERR_FAIL_COND(storage.frame.current_rt); + + GLES3::RenderTarget *rt = storage.render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + // TODO: do we need a keep 3d linear option? + + if (rt->external.fbo != 0) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo); + } else { + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); + } + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); + glBlitFramebuffer(0, 0, rt->width, rt->height, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); +} + +// is this p_screen useless in a multi window environment? +void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) { + // do this once off for all blits + storage.bind_framebuffer_system(); + + storage.frame.current_rt = nullptr; + + for (int i = 0; i < p_amount; i++) { + const BlitToScreen &blit = p_render_targets[i]; + + RID rid_rt = blit.render_target; + + Rect2 dst_rect = blit.dst_rect; + _blit_render_target_to_screen(rid_rt, p_screen, dst_rect); + } +} + +void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) { + if (p_image.is_null() || p_image->is_empty()) { + return; + } + + Size2i win_size = DisplayServer::get_singleton()->screen_get_size(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, win_size.width, win_size.height); + glDisable(GL_BLEND); + glDepthMask(GL_FALSE); + if (false) { + // if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { + glClearColor(0.0, 0.0, 0.0, 0.0); + } else { + glClearColor(p_color.r, p_color.g, p_color.b, 1.0); + } + glClear(GL_COLOR_BUFFER_BIT); + + canvas.canvas_begin(); + + RID texture = texture_storage.texture_create(); + //texture_storage.texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, p_use_filter ? VS::TEXTURE_FLAG_FILTER : 0); + texture_storage._texture_allocate_internal(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), RenderingDevice::TEXTURE_TYPE_2D); + texture_storage.texture_set_data(texture, p_image); + + Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); + Rect2 screenrect; + if (p_scale) { + if (win_size.width > win_size.height) { + //scale horizontally + screenrect.size.y = win_size.height; + screenrect.size.x = imgrect.size.x * win_size.height / imgrect.size.y; + screenrect.position.x = (win_size.width - screenrect.size.x) / 2; + + } else { + //scale vertically + screenrect.size.x = win_size.width; + screenrect.size.y = imgrect.size.y * win_size.width / imgrect.size.x; + screenrect.position.y = (win_size.height - screenrect.size.y) / 2; + } + } else { + screenrect = imgrect; + screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor(); + } + + GLES3::Texture *t = texture_storage.get_texture(texture); + glActiveTexture(GL_TEXTURE0 + config.max_texture_image_units - 1); + glBindTexture(GL_TEXTURE_2D, t->tex_id); + glBindTexture(GL_TEXTURE_2D, 0); + canvas.canvas_end(); + + texture_storage.texture_free(texture); + + end_frame(true); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h new file mode 100644 index 0000000000..4aa94e5293 --- /dev/null +++ b/drivers/gles3/rasterizer_gles3.h @@ -0,0 +1,102 @@ +/*************************************************************************/ +/* rasterizer_gles3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_OPENGL_H +#define RASTERIZER_OPENGL_H + +#ifdef GLES3_ENABLED + +#include "rasterizer_canvas_gles3.h" +#include "rasterizer_scene_gles3.h" +#include "rasterizer_storage_gles3.h" +#include "servers/rendering/renderer_compositor.h" +#include "storage/canvas_texture_storage.h" +#include "storage/config.h" +#include "storage/decal_atlas_storage.h" +#include "storage/render_target_storage.h" +#include "storage/texture_storage.h" + +class RasterizerGLES3 : public RendererCompositor { +private: + uint64_t frame = 1; + float delta = 0; + + double time_total = 0.0; + +protected: + GLES3::Config config; + GLES3::CanvasTextureStorage canvas_texture_storage; + GLES3::TextureStorage texture_storage; + GLES3::DecalAtlasStorage decal_atlas_storage; + RasterizerStorageGLES3 storage; + RasterizerCanvasGLES3 canvas; + RasterizerSceneGLES3 scene; + + void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect); + +public: + RendererCanvasTextureStorage *get_canvas_texture_storage() { return &canvas_texture_storage; } + RendererTextureStorage *get_texture_storage() { return &texture_storage; } + RendererDecalAtlasStorage *get_decal_atlas_storage() { return &decal_atlas_storage; } + RendererStorage *get_storage() { return &storage; } + RendererCanvasRender *get_canvas() { return &canvas; } + RendererSceneRender *get_scene() { return &scene; } + + void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true); + + void initialize(); + void begin_frame(double frame_step); + + void prepare_for_blitting_render_targets(); + void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount); + + void end_frame(bool p_swap_buffers); + + void finalize() {} + + static RendererCompositor *_create_current() { + return memnew(RasterizerGLES3); + } + + static void make_current() { + _create_func = _create_current; + } + + virtual bool is_low_end() const { return true; } + uint64_t get_frame_number() const { return frame; } + double get_frame_delta_time() const { return delta; } + + RasterizerGLES3(); + ~RasterizerGLES3() {} +}; + +#endif // GLES3_ENABLED + +#endif diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp new file mode 100644 index 0000000000..1382573461 --- /dev/null +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -0,0 +1,475 @@ +/*************************************************************************/ +/* rasterizer_scene_gles3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_scene_gles3.h" + +#ifdef GLES3_ENABLED + +// TODO: 3D support not implemented yet. + +RasterizerSceneGLES3::GeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_base) { + return nullptr; +} + +void RasterizerSceneGLES3::geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) { +} + +void RasterizerSceneGLES3::geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) { +} + +void RasterizerSceneGLES3::geometry_instance_set_material_overlay(GeometryInstance *p_geometry_instance, RID p_overlay) { +} + +void RasterizerSceneGLES3::geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_material) { +} + +void RasterizerSceneGLES3::geometry_instance_set_mesh_instance(GeometryInstance *p_geometry_instance, RID p_mesh_instance) { +} + +void RasterizerSceneGLES3::geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) { +} + +void RasterizerSceneGLES3::geometry_instance_set_layer_mask(GeometryInstance *p_geometry_instance, uint32_t p_layer_mask) { +} + +void RasterizerSceneGLES3::geometry_instance_set_lod_bias(GeometryInstance *p_geometry_instance, float p_lod_bias) { +} + +void RasterizerSceneGLES3::geometry_instance_set_transparency(GeometryInstance *p_geometry_instance, float p_transparency) { +} + +void RasterizerSceneGLES3::geometry_instance_set_fade_range(GeometryInstance *p_geometry_instance, bool p_enable_near, float p_near_begin, float p_near_end, bool p_enable_far, float p_far_begin, float p_far_end) { +} + +void RasterizerSceneGLES3::geometry_instance_set_parent_fade_alpha(GeometryInstance *p_geometry_instance, float p_alpha) { +} + +void RasterizerSceneGLES3::geometry_instance_set_use_baked_light(GeometryInstance *p_geometry_instance, bool p_enable) { +} + +void RasterizerSceneGLES3::geometry_instance_set_use_dynamic_gi(GeometryInstance *p_geometry_instance, bool p_enable) { +} + +void RasterizerSceneGLES3::geometry_instance_set_use_lightmap(GeometryInstance *p_geometry_instance, RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) { +} + +void RasterizerSceneGLES3::geometry_instance_set_lightmap_capture(GeometryInstance *p_geometry_instance, const Color *p_sh9) { +} + +void RasterizerSceneGLES3::geometry_instance_set_instance_shader_parameters_offset(GeometryInstance *p_geometry_instance, int32_t p_offset) { +} + +void RasterizerSceneGLES3::geometry_instance_set_cast_double_sided_shadows(GeometryInstance *p_geometry_instance, bool p_enable) { +} + +uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() { + return 0; +} + +void RasterizerSceneGLES3::geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) { +} + +void RasterizerSceneGLES3::geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) { +} + +void RasterizerSceneGLES3::geometry_instance_pair_decal_instances(GeometryInstance *p_geometry_instance, const RID *p_decal_instances, uint32_t p_decal_instance_count) { +} + +void RasterizerSceneGLES3::geometry_instance_pair_voxel_gi_instances(GeometryInstance *p_geometry_instance, const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) { +} + +void RasterizerSceneGLES3::geometry_instance_set_softshadow_projector_pairing(GeometryInstance *p_geometry_instance, bool p_softshadow, bool p_projector) { +} + +void RasterizerSceneGLES3::geometry_instance_free(GeometryInstance *p_geometry_instance) { +} + +/* SHADOW ATLAS API */ + +RID RasterizerSceneGLES3::shadow_atlas_create() { + return RID(); +} + +void RasterizerSceneGLES3::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits) { +} + +void RasterizerSceneGLES3::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) { +} + +bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) { + return false; +} + +void RasterizerSceneGLES3::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) { +} + +int RasterizerSceneGLES3::get_directional_light_shadow_size(RID p_light_intance) { + return 0; +} + +void RasterizerSceneGLES3::set_directional_shadow_count(int p_count) { +} + +/* SDFGI UPDATE */ + +void RasterizerSceneGLES3::sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) { +} + +int RasterizerSceneGLES3::sdfgi_get_pending_region_count(RID p_render_buffers) const { + return 0; +} + +AABB RasterizerSceneGLES3::sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const { + return AABB(); +} + +uint32_t RasterizerSceneGLES3::sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const { + return 0; +} + +/* SKY API */ + +RID RasterizerSceneGLES3::sky_allocate() { + return RID(); +} + +void RasterizerSceneGLES3::sky_initialize(RID p_rid) { +} + +void RasterizerSceneGLES3::sky_set_radiance_size(RID p_sky, int p_radiance_size) { +} + +void RasterizerSceneGLES3::sky_set_mode(RID p_sky, RS::SkyMode p_samples) { +} + +void RasterizerSceneGLES3::sky_set_material(RID p_sky, RID p_material) { +} + +Ref<Image> RasterizerSceneGLES3::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) { + return Ref<Image>(); +} + +/* ENVIRONMENT API */ + +RID RasterizerSceneGLES3::environment_allocate() { + return RID(); +} + +void RasterizerSceneGLES3::environment_initialize(RID p_rid) { +} + +void RasterizerSceneGLES3::environment_set_background(RID p_env, RS::EnvironmentBG p_bg) { +} + +void RasterizerSceneGLES3::environment_set_sky(RID p_env, RID p_sky) { +} + +void RasterizerSceneGLES3::environment_set_sky_custom_fov(RID p_env, float p_scale) { +} + +void RasterizerSceneGLES3::environment_set_sky_orientation(RID p_env, const Basis &p_orientation) { +} + +void RasterizerSceneGLES3::environment_set_bg_color(RID p_env, const Color &p_color) { +} + +void RasterizerSceneGLES3::environment_set_bg_energy(RID p_env, float p_energy) { +} + +void RasterizerSceneGLES3::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { +} + +void RasterizerSceneGLES3::environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source) { +} + +void RasterizerSceneGLES3::environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) { +} + +void RasterizerSceneGLES3::environment_glow_set_use_bicubic_upscale(bool p_enable) { +} + +void RasterizerSceneGLES3::environment_glow_set_use_high_quality(bool p_enable) { +} + +void RasterizerSceneGLES3::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) { +} + +void RasterizerSceneGLES3::environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) { +} + +void RasterizerSceneGLES3::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_power, float p_detail, float p_horizon, float p_sharpness, float p_light_affect, float p_ao_channel_affect) { +} + +void RasterizerSceneGLES3::environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { +} + +void RasterizerSceneGLES3::environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) { +} +void RasterizerSceneGLES3::environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { +} + +void RasterizerSceneGLES3::environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { +} + +void RasterizerSceneGLES3::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) { +} + +void RasterizerSceneGLES3::environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) { +} + +void RasterizerSceneGLES3::environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) { +} + +void RasterizerSceneGLES3::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) { +} + +void RasterizerSceneGLES3::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) { +} + +void RasterizerSceneGLES3::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) { +} + +void RasterizerSceneGLES3::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject) { +} + +void RasterizerSceneGLES3::environment_set_volumetric_fog_volume_size(int p_size, int p_depth) { +} + +void RasterizerSceneGLES3::environment_set_volumetric_fog_filter_active(bool p_enable) { +} + +Ref<Image> RasterizerSceneGLES3::environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) { + return Ref<Image>(); +} + +bool RasterizerSceneGLES3::is_environment(RID p_env) const { + return false; +} + +RS::EnvironmentBG RasterizerSceneGLES3::environment_get_background(RID p_env) const { + return RS::ENV_BG_KEEP; +} + +int RasterizerSceneGLES3::environment_get_canvas_max_layer(RID p_env) const { + return 0; +} + +RID RasterizerSceneGLES3::camera_effects_allocate() { + return RID(); +} + +void RasterizerSceneGLES3::camera_effects_initialize(RID p_rid) { +} + +void RasterizerSceneGLES3::camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) { +} + +void RasterizerSceneGLES3::camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) { +} + +void RasterizerSceneGLES3::camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) { +} + +void RasterizerSceneGLES3::camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) { +} + +void RasterizerSceneGLES3::shadows_quality_set(RS::ShadowQuality p_quality) { +} + +void RasterizerSceneGLES3::directional_shadow_quality_set(RS::ShadowQuality p_quality) { +} + +RID RasterizerSceneGLES3::light_instance_create(RID p_light) { + return RID(); +} + +void RasterizerSceneGLES3::light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) { +} + +void RasterizerSceneGLES3::light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) { +} + +void RasterizerSceneGLES3::light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) { +} + +void RasterizerSceneGLES3::light_instance_mark_visible(RID p_light_instance) { +} + +RID RasterizerSceneGLES3::fog_volume_instance_create(RID p_fog_volume) { + return RID(); +} + +void RasterizerSceneGLES3::fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) { +} + +void RasterizerSceneGLES3::fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) { +} + +RID RasterizerSceneGLES3::fog_volume_instance_get_volume(RID p_fog_volume_instance) const { + return RID(); +} + +Vector3 RasterizerSceneGLES3::fog_volume_instance_get_position(RID p_fog_volume_instance) const { + return Vector3(); +} + +RID RasterizerSceneGLES3::reflection_atlas_create() { + return RID(); +} + +int RasterizerSceneGLES3::reflection_atlas_get_size(RID p_ref_atlas) const { + return 0; +} + +void RasterizerSceneGLES3::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) { +} + +RID RasterizerSceneGLES3::reflection_probe_instance_create(RID p_probe) { + return RID(); +} + +void RasterizerSceneGLES3::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) { +} + +void RasterizerSceneGLES3::reflection_probe_release_atlas_index(RID p_instance) { +} + +bool RasterizerSceneGLES3::reflection_probe_instance_needs_redraw(RID p_instance) { + return false; +} + +bool RasterizerSceneGLES3::reflection_probe_instance_has_reflection(RID p_instance) { + return false; +} + +bool RasterizerSceneGLES3::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { + return false; +} + +bool RasterizerSceneGLES3::reflection_probe_instance_postprocess_step(RID p_instance) { + return true; +} + +RID RasterizerSceneGLES3::decal_instance_create(RID p_decal) { + return RID(); +} + +void RasterizerSceneGLES3::decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) { +} + +RID RasterizerSceneGLES3::lightmap_instance_create(RID p_lightmap) { + return RID(); +} + +void RasterizerSceneGLES3::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) { +} + +RID RasterizerSceneGLES3::voxel_gi_instance_create(RID p_voxel_gi) { + return RID(); +} + +void RasterizerSceneGLES3::voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) { +} + +bool RasterizerSceneGLES3::voxel_gi_needs_update(RID p_probe) const { + return false; +} + +void RasterizerSceneGLES3::voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects) { +} + +void RasterizerSceneGLES3::voxel_gi_set_quality(RS::VoxelGIQuality) { +} + +void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { +} + +void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { +} + +void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) { +} + +void RasterizerSceneGLES3::set_scene_pass(uint64_t p_pass) { +} + +void RasterizerSceneGLES3::set_time(double p_time, double p_step) { +} + +void RasterizerSceneGLES3::set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) { +} + +RID RasterizerSceneGLES3::render_buffers_create() { + return RID(); +} + +void RasterizerSceneGLES3::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_fsr_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) { +} + +void RasterizerSceneGLES3::gi_set_use_half_resolution(bool p_enable) { +} + +void RasterizerSceneGLES3::screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) { +} + +bool RasterizerSceneGLES3::screen_space_roughness_limiter_is_active() const { + return false; +} + +void RasterizerSceneGLES3::sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) { +} + +void RasterizerSceneGLES3::sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) { +} + +TypedArray<Image> RasterizerSceneGLES3::bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) { + return TypedArray<Image>(); +} + +bool RasterizerSceneGLES3::free(RID p_rid) { + return false; +} + +void RasterizerSceneGLES3::update() { +} + +void RasterizerSceneGLES3::sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) { +} + +void RasterizerSceneGLES3::decals_set_filter(RS::DecalFilter p_filter) { +} + +void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter p_filter) { +} + +RasterizerSceneGLES3::RasterizerSceneGLES3() { +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h new file mode 100644 index 0000000000..12bb21a5a0 --- /dev/null +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -0,0 +1,230 @@ +/*************************************************************************/ +/* rasterizer_scene_gles3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_SCENE_OPENGL_H +#define RASTERIZER_SCENE_OPENGL_H + +#ifdef GLES3_ENABLED + +#include "core/math/camera_matrix.h" +#include "core/templates/rid_owner.h" +#include "core/templates/self_list.h" +#include "scene/resources/mesh.h" +#include "servers/rendering/renderer_compositor.h" +#include "servers/rendering/renderer_scene_render.h" +#include "servers/rendering_server.h" + +class RasterizerSceneGLES3 : public RendererSceneRender { +public: + struct State { + //SceneShaderGLES3 scene_shader; + } state; + + GeometryInstance *geometry_instance_create(RID p_base) override; + void geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) override; + void geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) override; + void geometry_instance_set_material_overlay(GeometryInstance *p_geometry_instance, RID p_overlay) override; + void geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_material) override; + void geometry_instance_set_mesh_instance(GeometryInstance *p_geometry_instance, RID p_mesh_instance) override; + void geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) override; + void geometry_instance_set_layer_mask(GeometryInstance *p_geometry_instance, uint32_t p_layer_mask) override; + void geometry_instance_set_lod_bias(GeometryInstance *p_geometry_instance, float p_lod_bias) override; + void geometry_instance_set_transparency(GeometryInstance *p_geometry_instance, float p_transparency) override; + void geometry_instance_set_fade_range(GeometryInstance *p_geometry_instance, bool p_enable_near, float p_near_begin, float p_near_end, bool p_enable_far, float p_far_begin, float p_far_end) override; + void geometry_instance_set_parent_fade_alpha(GeometryInstance *p_geometry_instance, float p_alpha) override; + void geometry_instance_set_use_baked_light(GeometryInstance *p_geometry_instance, bool p_enable) override; + void geometry_instance_set_use_dynamic_gi(GeometryInstance *p_geometry_instance, bool p_enable) override; + void geometry_instance_set_use_lightmap(GeometryInstance *p_geometry_instance, RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override; + void geometry_instance_set_lightmap_capture(GeometryInstance *p_geometry_instance, const Color *p_sh9) override; + void geometry_instance_set_instance_shader_parameters_offset(GeometryInstance *p_geometry_instance, int32_t p_offset) override; + void geometry_instance_set_cast_double_sided_shadows(GeometryInstance *p_geometry_instance, bool p_enable) override; + + uint32_t geometry_instance_get_pair_mask() override; + void geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) override; + void geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override; + void geometry_instance_pair_decal_instances(GeometryInstance *p_geometry_instance, const RID *p_decal_instances, uint32_t p_decal_instance_count) override; + void geometry_instance_pair_voxel_gi_instances(GeometryInstance *p_geometry_instance, const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override; + void geometry_instance_set_softshadow_projector_pairing(GeometryInstance *p_geometry_instance, bool p_softshadow, bool p_projector) override; + + void geometry_instance_free(GeometryInstance *p_geometry_instance) override; + + /* SHADOW ATLAS API */ + + RID shadow_atlas_create() override; + void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override; + void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override; + bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override; + + void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override; + int get_directional_light_shadow_size(RID p_light_intance) override; + void set_directional_shadow_count(int p_count) override; + + /* SDFGI UPDATE */ + + void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) override; + int sdfgi_get_pending_region_count(RID p_render_buffers) const override; + AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override; + uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override; + + /* SKY API */ + + RID sky_allocate() override; + void sky_initialize(RID p_rid) override; + void sky_set_radiance_size(RID p_sky, int p_radiance_size) override; + void sky_set_mode(RID p_sky, RS::SkyMode p_samples) override; + void sky_set_material(RID p_sky, RID p_material) override; + Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override; + + /* ENVIRONMENT API */ + + RID environment_allocate() override; + void environment_initialize(RID p_rid) override; + void environment_set_background(RID p_env, RS::EnvironmentBG p_bg) override; + void environment_set_sky(RID p_env, RID p_sky) override; + void environment_set_sky_custom_fov(RID p_env, float p_scale) override; + void environment_set_sky_orientation(RID p_env, const Basis &p_orientation) override; + void environment_set_bg_color(RID p_env, const Color &p_color) override; + void environment_set_bg_energy(RID p_env, float p_energy) override; + void environment_set_canvas_max_layer(RID p_env, int p_max_layer) override; + void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG) override; + + void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) override; + void environment_glow_set_use_bicubic_upscale(bool p_enable) override; + void environment_glow_set_use_high_quality(bool p_enable) override; + + void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) override; + void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override; + void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_power, float p_detail, float p_horizon, float p_sharpness, float p_light_affect, float p_ao_channel_affect) override; + void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; + void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) override; + void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; + + void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override; + + void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override; + void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override; + void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override; + + void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) override; + + void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) override; + + void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) override; + void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject) override; + void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override; + void environment_set_volumetric_fog_filter_active(bool p_enable) override; + + Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override; + + bool is_environment(RID p_env) const override; + RS::EnvironmentBG environment_get_background(RID p_env) const override; + int environment_get_canvas_max_layer(RID p_env) const override; + + RID camera_effects_allocate() override; + void camera_effects_initialize(RID p_rid) override; + void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override; + void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override; + + void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override; + void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override; + + void shadows_quality_set(RS::ShadowQuality p_quality) override; + void directional_shadow_quality_set(RS::ShadowQuality p_quality) override; + + RID light_instance_create(RID p_light) override; + void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override; + void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override; + void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override; + void light_instance_mark_visible(RID p_light_instance) override; + + RID fog_volume_instance_create(RID p_fog_volume) override; + void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) override; + void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) override; + RID fog_volume_instance_get_volume(RID p_fog_volume_instance) const override; + Vector3 fog_volume_instance_get_position(RID p_fog_volume_instance) const override; + + RID reflection_atlas_create() override; + int reflection_atlas_get_size(RID p_ref_atlas) const override; + void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override; + + RID reflection_probe_instance_create(RID p_probe) override; + void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override; + void reflection_probe_release_atlas_index(RID p_instance) override; + bool reflection_probe_instance_needs_redraw(RID p_instance) override; + bool reflection_probe_instance_has_reflection(RID p_instance) override; + bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override; + bool reflection_probe_instance_postprocess_step(RID p_instance) override; + + RID decal_instance_create(RID p_decal) override; + void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override; + + RID lightmap_instance_create(RID p_lightmap) override; + void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override; + + RID voxel_gi_instance_create(RID p_voxel_gi) override; + void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) override; + bool voxel_gi_needs_update(RID p_probe) const override; + void voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects) override; + + void voxel_gi_set_quality(RS::VoxelGIQuality) override; + + void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; + void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; + void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) override; + + void set_scene_pass(uint64_t p_pass) override; + void set_time(double p_time, double p_step) override; + void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override; + + RID render_buffers_create() override; + void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_fsr_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) override; + void gi_set_use_half_resolution(bool p_enable) override; + + void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override; + bool screen_space_roughness_limiter_is_active() const override; + + void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override; + void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override; + + TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) override; + + bool free(RID p_rid) override; + void update() override; + void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override; + + void decals_set_filter(RS::DecalFilter p_filter) override; + void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override; + + RasterizerSceneGLES3(); +}; + +#endif // GLES3_ENABLED + +#endif // RASTERIZER_SCENE_OPENGL_H diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp new file mode 100644 index 0000000000..5efbc715ab --- /dev/null +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -0,0 +1,3156 @@ +/*************************************************************************/ +/* rasterizer_storage_gles3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rasterizer_storage_gles3.h" + +#ifdef GLES3_ENABLED + +#include "core/config/project_settings.h" +#include "core/math/transform_3d.h" +#include "rasterizer_canvas_gles3.h" +#include "rasterizer_scene_gles3.h" +#include "servers/rendering/shader_language.h" + +GLuint RasterizerStorageGLES3::system_fbo = 0; + +void RasterizerStorageGLES3::bind_quad_array() const { + //glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); + //glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); + //glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(8)); + + //glEnableVertexAttribArray(RS::ARRAY_VERTEX); + //glEnableVertexAttribArray(RS::ARRAY_TEX_UV); +} + +RID RasterizerStorageGLES3::sky_create() { + Sky *sky = memnew(Sky); + sky->radiance = 0; + return sky_owner.make_rid(sky); +} + +void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size) { +} + +/* SHADER API */ + +RID RasterizerStorageGLES3::shader_allocate() { + Shader *shader = memnew(Shader); + shader->mode = RS::SHADER_CANVAS_ITEM; + //shader->shader = &scene->state.scene_shader; + RID rid = shader_owner.make_rid(shader); + _shader_make_dirty(shader); + shader->self = rid; + + return rid; +} + +void RasterizerStorageGLES3::shader_initialize(RID p_rid) { + // noop +} + +//RID RasterizerStorageGLES3::shader_create() { +// Shader *shader = memnew(Shader); +// shader->mode = RS::SHADER_SPATIAL; +// shader->shader = &scene->state.scene_shader; +// RID rid = shader_owner.make_rid(shader); +// _shader_make_dirty(shader); +// shader->self = rid; + +// return rid; +//} + +void RasterizerStorageGLES3::_shader_make_dirty(Shader *p_shader) { + if (p_shader->dirty_list.in_list()) { + return; + } + + _shader_dirty_list.add(&p_shader->dirty_list); +} + +void RasterizerStorageGLES3::shader_set_code(RID p_shader, const String &p_code) { + Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND(!shader); + + shader->code = p_code; + + String mode_string = ShaderLanguage::get_shader_type(p_code); + RS::ShaderMode mode; + + if (mode_string == "canvas_item") { + mode = RS::SHADER_CANVAS_ITEM; + } else if (mode_string == "particles") { + mode = RS::SHADER_PARTICLES; + } else if (mode_string == "sky") { + mode = RS::SHADER_SKY; + } else if (mode_string == "spatial") { + mode = RS::SHADER_SPATIAL; + } else { + mode = RS::SHADER_MAX; + ERR_PRINT("shader type " + mode_string + " not supported in OpenGL renderer"); + } + + if (shader->version.is_valid() && mode != shader->mode) { + shader->shader->version_free(shader->version); + shader->version = RID(); + } + + shader->mode = mode; + + // TODO handle all shader types + if (mode == RS::SHADER_CANVAS_ITEM) { + shader->shader = &canvas->state.canvas_shader; + + } else if (mode == RS::SHADER_SPATIAL) { + //shader->shader = &scene->state.scene_shader; + } else if (mode == RS::SHADER_PARTICLES) { + } else if (mode == RS::SHADER_SKY) { + } else { + return; + } + + if (shader->version.is_null() && shader->shader) { + shader->version = shader->shader->version_create(); + } + + _shader_make_dirty(shader); +} + +String RasterizerStorageGLES3::shader_get_code(RID p_shader) const { + const Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND_V(!shader, ""); + + return shader->code; +} + +void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { + _shader_dirty_list.remove(&p_shader->dirty_list); + + p_shader->valid = false; + + p_shader->uniforms.clear(); + + if (p_shader->code.is_empty()) { + return; //just invalid, but no error + } + + ShaderCompiler::GeneratedCode gen_code; + ShaderCompiler::IdentifierActions *actions = nullptr; + + switch (p_shader->mode) { + case RS::SHADER_CANVAS_ITEM: { + p_shader->canvas_item.light_mode = Shader::CanvasItem::LIGHT_MODE_NORMAL; + p_shader->canvas_item.blend_mode = Shader::CanvasItem::BLEND_MODE_MIX; + + p_shader->canvas_item.uses_screen_texture = false; + p_shader->canvas_item.uses_screen_uv = false; + p_shader->canvas_item.uses_time = false; + p_shader->canvas_item.uses_modulate = false; + p_shader->canvas_item.uses_color = false; + p_shader->canvas_item.uses_vertex = false; + + p_shader->canvas_item.uses_model_matrix = false; + p_shader->canvas_item.uses_extra_matrix = false; + p_shader->canvas_item.uses_projection_matrix = false; + p_shader->canvas_item.uses_instance_custom = false; + + shaders.actions_canvas.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_ADD); + shaders.actions_canvas.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MIX); + shaders.actions_canvas.render_mode_values["blend_sub"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_SUB); + shaders.actions_canvas.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MUL); + shaders.actions_canvas.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_PMALPHA); + + shaders.actions_canvas.render_mode_values["unshaded"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_UNSHADED); + shaders.actions_canvas.render_mode_values["light_only"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY); + + shaders.actions_canvas.usage_flag_pointers["SCREEN_UV"] = &p_shader->canvas_item.uses_screen_uv; + shaders.actions_canvas.usage_flag_pointers["SCREEN_PIXEL_SIZE"] = &p_shader->canvas_item.uses_screen_uv; + shaders.actions_canvas.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->canvas_item.uses_screen_texture; + shaders.actions_canvas.usage_flag_pointers["TIME"] = &p_shader->canvas_item.uses_time; + shaders.actions_canvas.usage_flag_pointers["MODULATE"] = &p_shader->canvas_item.uses_modulate; + shaders.actions_canvas.usage_flag_pointers["COLOR"] = &p_shader->canvas_item.uses_color; + + shaders.actions_canvas.usage_flag_pointers["VERTEX"] = &p_shader->canvas_item.uses_vertex; + + shaders.actions_canvas.usage_flag_pointers["MODEL_MATRIX"] = &p_shader->canvas_item.uses_model_matrix; + shaders.actions_canvas.usage_flag_pointers["EXTRA_MATRIX"] = &p_shader->canvas_item.uses_extra_matrix; + shaders.actions_canvas.usage_flag_pointers["PROJECTION_MATRIX"] = &p_shader->canvas_item.uses_projection_matrix; + shaders.actions_canvas.usage_flag_pointers["INSTANCE_CUSTOM"] = &p_shader->canvas_item.uses_instance_custom; + + actions = &shaders.actions_canvas; + actions->uniforms = &p_shader->uniforms; + } break; + + case RS::SHADER_SPATIAL: { + // TODO remove once 3D is added back + return; + p_shader->spatial.blend_mode = Shader::Spatial::BLEND_MODE_MIX; + p_shader->spatial.depth_draw_mode = Shader::Spatial::DEPTH_DRAW_OPAQUE; + p_shader->spatial.cull_mode = Shader::Spatial::CULL_MODE_BACK; + p_shader->spatial.uses_alpha = false; + p_shader->spatial.uses_alpha_scissor = false; + p_shader->spatial.uses_discard = false; + p_shader->spatial.unshaded = false; + p_shader->spatial.no_depth_test = false; + p_shader->spatial.uses_sss = false; + p_shader->spatial.uses_time = false; + p_shader->spatial.uses_vertex_lighting = false; + p_shader->spatial.uses_screen_texture = false; + p_shader->spatial.uses_depth_texture = false; + p_shader->spatial.uses_vertex = false; + p_shader->spatial.uses_tangent = false; + p_shader->spatial.uses_ensure_correct_normals = false; + p_shader->spatial.writes_modelview_or_projection = false; + p_shader->spatial.uses_world_coordinates = false; + + shaders.actions_scene.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_ADD); + shaders.actions_scene.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MIX); + shaders.actions_scene.render_mode_values["blend_sub"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_SUB); + shaders.actions_scene.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MUL); + + shaders.actions_scene.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_OPAQUE); + shaders.actions_scene.render_mode_values["depth_draw_always"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALWAYS); + shaders.actions_scene.render_mode_values["depth_draw_never"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_NEVER); + shaders.actions_scene.render_mode_values["depth_draw_alpha_prepass"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS); + + shaders.actions_scene.render_mode_values["cull_front"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_FRONT); + shaders.actions_scene.render_mode_values["cull_back"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_BACK); + shaders.actions_scene.render_mode_values["cull_disabled"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_DISABLED); + + shaders.actions_scene.render_mode_flags["unshaded"] = &p_shader->spatial.unshaded; + shaders.actions_scene.render_mode_flags["depth_test_disable"] = &p_shader->spatial.no_depth_test; + + shaders.actions_scene.render_mode_flags["vertex_lighting"] = &p_shader->spatial.uses_vertex_lighting; + + shaders.actions_scene.render_mode_flags["world_vertex_coords"] = &p_shader->spatial.uses_world_coordinates; + + shaders.actions_scene.render_mode_flags["ensure_correct_normals"] = &p_shader->spatial.uses_ensure_correct_normals; + + shaders.actions_scene.usage_flag_pointers["ALPHA"] = &p_shader->spatial.uses_alpha; + shaders.actions_scene.usage_flag_pointers["ALPHA_SCISSOR"] = &p_shader->spatial.uses_alpha_scissor; + + shaders.actions_scene.usage_flag_pointers["SSS_STRENGTH"] = &p_shader->spatial.uses_sss; + shaders.actions_scene.usage_flag_pointers["DISCARD"] = &p_shader->spatial.uses_discard; + shaders.actions_scene.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->spatial.uses_screen_texture; + shaders.actions_scene.usage_flag_pointers["DEPTH_TEXTURE"] = &p_shader->spatial.uses_depth_texture; + shaders.actions_scene.usage_flag_pointers["TIME"] = &p_shader->spatial.uses_time; + + // Use of any of these BUILTINS indicate the need for transformed tangents. + // This is needed to know when to transform tangents in software skinning. + shaders.actions_scene.usage_flag_pointers["TANGENT"] = &p_shader->spatial.uses_tangent; + shaders.actions_scene.usage_flag_pointers["NORMALMAP"] = &p_shader->spatial.uses_tangent; + + shaders.actions_scene.write_flag_pointers["MODELVIEW_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; + shaders.actions_scene.write_flag_pointers["PROJECTION_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; + shaders.actions_scene.write_flag_pointers["VERTEX"] = &p_shader->spatial.uses_vertex; + + actions = &shaders.actions_scene; + actions->uniforms = &p_shader->uniforms; + } break; + + default: { + return; + } break; + } + + Error err = shaders.compiler.compile(p_shader->mode, p_shader->code, actions, p_shader->path, gen_code); + if (err != OK) { + return; + } + + Vector<StringName> texture_uniform_names; + for (int i = 0; i < gen_code.texture_uniforms.size(); i++) { + texture_uniform_names.push_back(gen_code.texture_uniforms[i].name); + } + + p_shader->shader->version_set_code(p_shader->version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_names); + + p_shader->texture_uniforms = gen_code.texture_uniforms; + + p_shader->uses_vertex_time = gen_code.uses_vertex_time; + p_shader->uses_fragment_time = gen_code.uses_fragment_time; + + for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) { + _material_make_dirty(E->self()); + } + + p_shader->valid = true; +} + +void RasterizerStorageGLES3::update_dirty_shaders() { + while (_shader_dirty_list.first()) { + _update_shader(_shader_dirty_list.first()->self()); + } +} + +void RasterizerStorageGLES3::shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const { + Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND(!shader); + + if (shader->dirty_list.in_list()) { + _update_shader(shader); + } + + Map<int, StringName> order; + + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = shader->uniforms.front(); E; E = E->next()) { + if (E->get().texture_order >= 0) { + order[E->get().texture_order + 100000] = E->key(); + } else { + order[E->get().order] = E->key(); + } + } + + for (Map<int, StringName>::Element *E = order.front(); E; E = E->next()) { + PropertyInfo pi; + ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[E->get()]; + + pi.name = E->get(); + + switch (u.type) { + case ShaderLanguage::TYPE_VOID: { + pi.type = Variant::NIL; + } break; + + case ShaderLanguage::TYPE_BOOL: { + pi.type = Variant::BOOL; + } break; + + // bool vectors + case ShaderLanguage::TYPE_BVEC2: { + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y"; + } break; + case ShaderLanguage::TYPE_BVEC3: { + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z"; + } break; + case ShaderLanguage::TYPE_BVEC4: { + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z,w"; + } break; + + // int stuff + case ShaderLanguage::TYPE_UINT: + case ShaderLanguage::TYPE_INT: { + pi.type = Variant::INT; + + if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(u.hint_range[0]) + "," + rtos(u.hint_range[1]) + "," + rtos(u.hint_range[2]); + } + } break; + + case ShaderLanguage::TYPE_IVEC2: + case ShaderLanguage::TYPE_UVEC2: + case ShaderLanguage::TYPE_IVEC3: + case ShaderLanguage::TYPE_UVEC3: + case ShaderLanguage::TYPE_IVEC4: + case ShaderLanguage::TYPE_UVEC4: { + // not sure what this should be in godot 4 + // pi.type = Variant::POOL_INT_ARRAY; + pi.type = Variant::PACKED_INT32_ARRAY; + } break; + + case ShaderLanguage::TYPE_FLOAT: { + pi.type = Variant::FLOAT; + if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(u.hint_range[0]) + "," + rtos(u.hint_range[1]) + "," + rtos(u.hint_range[2]); + } + } break; + + case ShaderLanguage::TYPE_VEC2: { + pi.type = Variant::VECTOR2; + } break; + case ShaderLanguage::TYPE_VEC3: { + pi.type = Variant::VECTOR3; + } break; + + case ShaderLanguage::TYPE_VEC4: { + if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + pi.type = Variant::COLOR; + } else { + pi.type = Variant::PLANE; + } + } break; + + case ShaderLanguage::TYPE_MAT2: { + pi.type = Variant::TRANSFORM2D; + } break; + + case ShaderLanguage::TYPE_MAT3: { + pi.type = Variant::BASIS; + } break; + + case ShaderLanguage::TYPE_MAT4: { + pi.type = Variant::TRANSFORM3D; + } break; + + case ShaderLanguage::TYPE_SAMPLER2D: + // case ShaderLanguage::TYPE_SAMPLEREXT: + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER2D: { + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Texture"; + } break; + + case ShaderLanguage::TYPE_SAMPLERCUBE: { + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "CubeMap"; + } break; + + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + // Not implemented in OpenGL + } break; + // new for godot 4 + case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: + case ShaderLanguage::TYPE_STRUCT: + case ShaderLanguage::TYPE_MAX: { + } break; + } + + p_param_list->push_back(pi); + } +} + +void RasterizerStorageGLES3::shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture, int p_index) { + Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND(!shader); + ERR_FAIL_COND(p_texture.is_valid() && !GLES3::TextureStorage::get_singleton()->owns_texture(p_texture)); + + if (!p_texture.is_valid()) { + if (shader->default_textures.has(p_name) && shader->default_textures[p_name].has(p_index)) { + shader->default_textures[p_name].erase(p_index); + + if (shader->default_textures[p_name].is_empty()) { + shader->default_textures.erase(p_name); + } + } + } else { + if (!shader->default_textures.has(p_name)) { + shader->default_textures[p_name] = Map<int, RID>(); + } + shader->default_textures[p_name][p_index] = p_texture; + } + + _shader_make_dirty(shader); +} + +RID RasterizerStorageGLES3::shader_get_default_texture_param(RID p_shader, const StringName &p_name, int p_index) const { + const Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND_V(!shader, RID()); + + if (shader->default_textures.has(p_name) && shader->default_textures[p_name].has(p_index)) { + return shader->default_textures[p_name][p_index]; + } + + return RID(); +} + +/* COMMON MATERIAL API */ + +void RasterizerStorageGLES3::_material_make_dirty(Material *p_material) const { + if (p_material->dirty_list.in_list()) { + return; + } + + _material_dirty_list.add(&p_material->dirty_list); +} + +RID RasterizerStorageGLES3::material_allocate() { + Material *material = memnew(Material); + return material_owner.make_rid(material); +} + +void RasterizerStorageGLES3::material_initialize(RID p_rid) { +} + +//RID RasterizerStorageGLES3::material_create() { +// Material *material = memnew(Material); + +// return material_owner.make_rid(material); +//} + +void RasterizerStorageGLES3::material_set_shader(RID p_material, RID p_shader) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + Shader *shader = shader_owner.get_or_null(p_shader); + + if (material->shader) { + // if a shader is present, remove the old shader + material->shader->materials.remove(&material->list); + } + + material->shader = shader; + + if (shader) { + shader->materials.add(&material->list); + } + + _material_make_dirty(material); +} + +RID RasterizerStorageGLES3::material_get_shader(RID p_material) const { + const Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, RID()); + + if (material->shader) { + return material->shader->self; + } + + return RID(); +} + +void RasterizerStorageGLES3::material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + if (p_value.get_type() == Variant::NIL) { + material->params.erase(p_param); + } else { + material->params[p_param] = p_value; + } + + _material_make_dirty(material); +} + +Variant RasterizerStorageGLES3::material_get_param(RID p_material, const StringName &p_param) const { + const Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, RID()); + + if (material->params.has(p_param)) { + return material->params[p_param]; + } + + return material_get_param_default(p_material, p_param); +} + +Variant RasterizerStorageGLES3::material_get_param_default(RID p_material, const StringName &p_param) const { + const Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, Variant()); + + if (material->shader) { + if (material->shader->uniforms.has(p_param)) { + ShaderLanguage::ShaderNode::Uniform uniform = material->shader->uniforms[p_param]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.hint); + } + } + return Variant(); +} + +void RasterizerStorageGLES3::material_set_line_width(RID p_material, float p_width) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + material->line_width = p_width; +} + +void RasterizerStorageGLES3::material_set_next_pass(RID p_material, RID p_next_material) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + material->next_pass = p_next_material; +} + +bool RasterizerStorageGLES3::material_is_animated(RID p_material) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->dirty_list.in_list()) { + _update_material(material); + } + + bool animated = material->is_animated_cache; + if (!animated && material->next_pass.is_valid()) { + animated = material_is_animated(material->next_pass); + } + return animated; +} + +bool RasterizerStorageGLES3::material_casts_shadows(RID p_material) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->dirty_list.in_list()) { + _update_material(material); + } + + bool casts_shadows = material->can_cast_shadow_cache; + + if (!casts_shadows && material->next_pass.is_valid()) { + casts_shadows = material_casts_shadows(material->next_pass); + } + + return casts_shadows; +} + +bool RasterizerStorageGLES3::material_uses_tangents(RID p_material) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, false); + + if (!material->shader) { + return false; + } + + if (material->shader->dirty_list.in_list()) { + _update_shader(material->shader); + } + + return material->shader->spatial.uses_tangent; +} + +bool RasterizerStorageGLES3::material_uses_ensure_correct_normals(RID p_material) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, false); + + if (!material->shader) { + return false; + } + + if (material->shader->dirty_list.in_list()) { + _update_shader(material->shader); + } + + return material->shader->spatial.uses_ensure_correct_normals; +} + +void RasterizerStorageGLES3::material_add_instance_owner(RID p_material, DependencyTracker *p_instance) { + /* + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + Map<InstanceBaseDependency *, int>::Element *E = material->instance_owners.find(p_instance); + if (E) { + E->get()++; + } else { + material->instance_owners[p_instance] = 1; + } +*/ +} + +void RasterizerStorageGLES3::material_remove_instance_owner(RID p_material, DependencyTracker *p_instance) { + /* + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + Map<InstanceBaseDependency *, int>::Element *E = material->instance_owners.find(p_instance); + ERR_FAIL_COND(!E); + + E->get()--; + + if (E->get() == 0) { + material->instance_owners.erase(E); + } +*/ +} + +void RasterizerStorageGLES3::material_set_render_priority(RID p_material, int priority) { + ERR_FAIL_COND(priority < RS::MATERIAL_RENDER_PRIORITY_MIN); + ERR_FAIL_COND(priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + material->render_priority = priority; +} + +void RasterizerStorageGLES3::_update_material(Material *p_material) { + if (p_material->dirty_list.in_list()) { + _material_dirty_list.remove(&p_material->dirty_list); + } + + if (p_material->shader && p_material->shader->dirty_list.in_list()) { + _update_shader(p_material->shader); + } + + if (p_material->shader && !p_material->shader->valid) { + return; + } + + { + bool can_cast_shadow = false; + bool is_animated = false; + + if (p_material->shader && p_material->shader->mode == RS::SHADER_SPATIAL) { + if (p_material->shader->spatial.blend_mode == Shader::Spatial::BLEND_MODE_MIX && + (!p_material->shader->spatial.uses_alpha || p_material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) { + can_cast_shadow = true; + } + + if (p_material->shader->spatial.uses_discard && p_material->shader->uses_fragment_time) { + is_animated = true; + } + + if (p_material->shader->spatial.uses_vertex && p_material->shader->uses_vertex_time) { + is_animated = true; + } + + if (can_cast_shadow != p_material->can_cast_shadow_cache || is_animated != p_material->is_animated_cache) { + p_material->can_cast_shadow_cache = can_cast_shadow; + p_material->is_animated_cache = is_animated; + + /* + for (Map<Geometry *, int>::Element *E = p_material->geometry_owners.front(); E; E = E->next()) { + E->key()->material_changed_notify(); + } + + for (Map<InstanceBaseDependency *, int>::Element *E = p_material->instance_owners.front(); E; E = E->next()) { + E->key()->base_changed(false, true); + } + */ + } + } + } + + // uniforms and other things will be set in the use_material method in ShaderGLES3 + + if (p_material->shader && p_material->shader->texture_uniforms.size() > 0) { + p_material->textures.resize(p_material->shader->texture_uniforms.size()); + + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = p_material->shader->uniforms.front(); E; E = E->next()) { + if (E->get().texture_order < 0) { + continue; // not a texture, does not go here + } + + RID texture; + + Map<StringName, Variant>::Element *V = p_material->params.find(E->key()); + + if (V) { + texture = V->get(); + } + + if (!texture.is_valid()) { + Map<StringName, Map<int, RID>>::Element *W = p_material->shader->default_textures.find(E->key()); + + // TODO: make texture uniform array properly works with GLES3 + if (W && W->get().has(0)) { + texture = W->get()[0]; + } + } + + p_material->textures.write[E->get().texture_order] = Pair<StringName, RID>(E->key(), texture); + } + } else { + p_material->textures.clear(); + } +} +/* +void RasterizerStorageGLES3::_material_add_geometry(RID p_material, Geometry *p_geometry) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); + + if (I) { + I->get()++; + } else { + material->geometry_owners[p_geometry] = 1; + } +} + +void RasterizerStorageGLES3::_material_remove_geometry(RID p_material, Geometry *p_geometry) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); + ERR_FAIL_COND(!I); + + I->get()--; + + if (I->get() == 0) { + material->geometry_owners.erase(I); + } +} +*/ +void RasterizerStorageGLES3::update_dirty_materials() { + while (_material_dirty_list.first()) { + Material *material = _material_dirty_list.first()->self(); + _update_material(material); + } +} + +/* MESH API */ + +RID RasterizerStorageGLES3::mesh_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::mesh_initialize(RID p_rid) { +} + +void RasterizerStorageGLES3::mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) { +} + +bool RasterizerStorageGLES3::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) { + return false; +} + +RID RasterizerStorageGLES3::mesh_instance_create(RID p_base) { + return RID(); +} + +void RasterizerStorageGLES3::mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) { +} + +void RasterizerStorageGLES3::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) { +} + +void RasterizerStorageGLES3::mesh_instance_check_for_update(RID p_mesh_instance) { +} + +void RasterizerStorageGLES3::update_mesh_instances() { +} + +void RasterizerStorageGLES3::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) { +} + +float RasterizerStorageGLES3::reflection_probe_get_mesh_lod_threshold(RID p_probe) const { + return 0.0; +} + +void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) { +} + +int RasterizerStorageGLES3::mesh_get_blend_shape_count(RID p_mesh) const { + return 0; +} + +void RasterizerStorageGLES3::mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) { +} + +RS::BlendShapeMode RasterizerStorageGLES3::mesh_get_blend_shape_mode(RID p_mesh) const { + return RS::BLEND_SHAPE_MODE_NORMALIZED; +} + +void RasterizerStorageGLES3::mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +} + +void RasterizerStorageGLES3::mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +} + +void RasterizerStorageGLES3::mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +} + +void RasterizerStorageGLES3::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { +} + +RID RasterizerStorageGLES3::mesh_surface_get_material(RID p_mesh, int p_surface) const { + return RID(); +} + +RS::SurfaceData RasterizerStorageGLES3::mesh_get_surface(RID p_mesh, int p_surface) const { + return RS::SurfaceData(); +} + +int RasterizerStorageGLES3::mesh_get_surface_count(RID p_mesh) const { + return 1; +} + +void RasterizerStorageGLES3::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { +} + +AABB RasterizerStorageGLES3::mesh_get_custom_aabb(RID p_mesh) const { + return AABB(); +} + +AABB RasterizerStorageGLES3::mesh_get_aabb(RID p_mesh, RID p_skeleton) { + return AABB(); +} + +void RasterizerStorageGLES3::mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) { +} + +void RasterizerStorageGLES3::mesh_clear(RID p_mesh) { +} + +/* MULTIMESH API */ + +RID RasterizerStorageGLES3::multimesh_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::multimesh_initialize(RID p_rid) { +} + +void RasterizerStorageGLES3::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { +} + +int RasterizerStorageGLES3::multimesh_get_instance_count(RID p_multimesh) const { + return 0; +} + +void RasterizerStorageGLES3::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { +} + +void RasterizerStorageGLES3::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { +} + +void RasterizerStorageGLES3::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { +} + +void RasterizerStorageGLES3::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { +} + +void RasterizerStorageGLES3::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { +} + +RID RasterizerStorageGLES3::multimesh_get_mesh(RID p_multimesh) const { + return RID(); +} + +AABB RasterizerStorageGLES3::multimesh_get_aabb(RID p_multimesh) const { + return AABB(); +} + +Transform3D RasterizerStorageGLES3::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { + return Transform3D(); +} + +Transform2D RasterizerStorageGLES3::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { + return Transform2D(); +} + +Color RasterizerStorageGLES3::multimesh_instance_get_color(RID p_multimesh, int p_index) const { + return Color(); +} + +Color RasterizerStorageGLES3::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { + return Color(); +} + +void RasterizerStorageGLES3::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { +} + +Vector<float> RasterizerStorageGLES3::multimesh_get_buffer(RID p_multimesh) const { + return Vector<float>(); +} + +void RasterizerStorageGLES3::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { +} + +int RasterizerStorageGLES3::multimesh_get_visible_instances(RID p_multimesh) const { + return 0; +} + +/* SKELETON API */ + +RID RasterizerStorageGLES3::skeleton_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::skeleton_initialize(RID p_rid) { +} + +void RasterizerStorageGLES3::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton) { +} + +void RasterizerStorageGLES3::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { +} + +int RasterizerStorageGLES3::skeleton_get_bone_count(RID p_skeleton) const { + return 0; +} + +void RasterizerStorageGLES3::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) { +} + +Transform3D RasterizerStorageGLES3::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { + return Transform3D(); +} + +void RasterizerStorageGLES3::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { +} + +Transform2D RasterizerStorageGLES3::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { + return Transform2D(); +} + +/* Light API */ + +RID RasterizerStorageGLES3::directional_light_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::directional_light_initialize(RID p_rid) { +} + +RID RasterizerStorageGLES3::omni_light_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::omni_light_initialize(RID p_rid) { +} + +RID RasterizerStorageGLES3::spot_light_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::spot_light_initialize(RID p_rid) { +} + +RID RasterizerStorageGLES3::reflection_probe_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::reflection_probe_initialize(RID p_rid) { +} + +void RasterizerStorageGLES3::light_set_color(RID p_light, const Color &p_color) { +} + +void RasterizerStorageGLES3::light_set_param(RID p_light, RS::LightParam p_param, float p_value) { +} + +void RasterizerStorageGLES3::light_set_shadow(RID p_light, bool p_enabled) { +} + +void RasterizerStorageGLES3::light_set_projector(RID p_light, RID p_texture) { +} + +void RasterizerStorageGLES3::light_set_negative(RID p_light, bool p_enable) { +} + +void RasterizerStorageGLES3::light_set_cull_mask(RID p_light, uint32_t p_mask) { +} + +void RasterizerStorageGLES3::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) { +} + +void RasterizerStorageGLES3::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) { +} + +void RasterizerStorageGLES3::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) { +} + +void RasterizerStorageGLES3::light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) { +} + +void RasterizerStorageGLES3::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) { +} + +void RasterizerStorageGLES3::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) { +} + +void RasterizerStorageGLES3::light_directional_set_blend_splits(RID p_light, bool p_enable) { +} + +bool RasterizerStorageGLES3::light_directional_get_blend_splits(RID p_light) const { + return false; +} + +void RasterizerStorageGLES3::light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) { +} + +RS::LightDirectionalSkyMode RasterizerStorageGLES3::light_directional_get_sky_mode(RID p_light) const { + return RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY; +} + +RS::LightDirectionalShadowMode RasterizerStorageGLES3::light_directional_get_shadow_mode(RID p_light) { + return RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; +} + +RS::LightOmniShadowMode RasterizerStorageGLES3::light_omni_get_shadow_mode(RID p_light) { + return RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; +} + +bool RasterizerStorageGLES3::light_has_shadow(RID p_light) const { + return false; +} + +bool RasterizerStorageGLES3::light_has_projector(RID p_light) const { + return false; +} + +RS::LightType RasterizerStorageGLES3::light_get_type(RID p_light) const { + return RS::LIGHT_OMNI; +} + +AABB RasterizerStorageGLES3::light_get_aabb(RID p_light) const { + return AABB(); +} + +float RasterizerStorageGLES3::light_get_param(RID p_light, RS::LightParam p_param) { + return 0.0; +} + +Color RasterizerStorageGLES3::light_get_color(RID p_light) { + return Color(); +} + +RS::LightBakeMode RasterizerStorageGLES3::light_get_bake_mode(RID p_light) { + return RS::LIGHT_BAKE_DISABLED; +} + +uint32_t RasterizerStorageGLES3::light_get_max_sdfgi_cascade(RID p_light) { + return 0; +} + +uint64_t RasterizerStorageGLES3::light_get_version(RID p_light) const { + return 0; +} + +/* PROBE API */ + +void RasterizerStorageGLES3::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) { +} + +void RasterizerStorageGLES3::reflection_probe_set_intensity(RID p_probe, float p_intensity) { +} + +void RasterizerStorageGLES3::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) { +} + +void RasterizerStorageGLES3::reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) { +} + +void RasterizerStorageGLES3::reflection_probe_set_ambient_energy(RID p_probe, float p_energy) { +} + +void RasterizerStorageGLES3::reflection_probe_set_max_distance(RID p_probe, float p_distance) { +} + +void RasterizerStorageGLES3::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) { +} + +void RasterizerStorageGLES3::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) { +} + +void RasterizerStorageGLES3::reflection_probe_set_as_interior(RID p_probe, bool p_enable) { +} + +void RasterizerStorageGLES3::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) { +} + +void RasterizerStorageGLES3::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) { +} + +void RasterizerStorageGLES3::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { +} + +void RasterizerStorageGLES3::reflection_probe_set_resolution(RID p_probe, int p_resolution) { +} + +AABB RasterizerStorageGLES3::reflection_probe_get_aabb(RID p_probe) const { + return AABB(); +} + +RS::ReflectionProbeUpdateMode RasterizerStorageGLES3::reflection_probe_get_update_mode(RID p_probe) const { + return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE; +} + +uint32_t RasterizerStorageGLES3::reflection_probe_get_cull_mask(RID p_probe) const { + return 0; +} + +Vector3 RasterizerStorageGLES3::reflection_probe_get_extents(RID p_probe) const { + return Vector3(); +} + +Vector3 RasterizerStorageGLES3::reflection_probe_get_origin_offset(RID p_probe) const { + return Vector3(); +} + +float RasterizerStorageGLES3::reflection_probe_get_origin_max_distance(RID p_probe) const { + return 0.0; +} + +bool RasterizerStorageGLES3::reflection_probe_renders_shadows(RID p_probe) const { + return false; +} + +void RasterizerStorageGLES3::base_update_dependency(RID p_base, DependencyTracker *p_instance) { +} + +void RasterizerStorageGLES3::skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) { +} + +/* VOXEL GI API */ + +RID RasterizerStorageGLES3::voxel_gi_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::voxel_gi_initialize(RID p_rid) { +} + +void RasterizerStorageGLES3::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) { +} + +AABB RasterizerStorageGLES3::voxel_gi_get_bounds(RID p_voxel_gi) const { + return AABB(); +} + +Vector3i RasterizerStorageGLES3::voxel_gi_get_octree_size(RID p_voxel_gi) const { + return Vector3i(); +} + +Vector<uint8_t> RasterizerStorageGLES3::voxel_gi_get_octree_cells(RID p_voxel_gi) const { + return Vector<uint8_t>(); +} + +Vector<uint8_t> RasterizerStorageGLES3::voxel_gi_get_data_cells(RID p_voxel_gi) const { + return Vector<uint8_t>(); +} + +Vector<uint8_t> RasterizerStorageGLES3::voxel_gi_get_distance_field(RID p_voxel_gi) const { + return Vector<uint8_t>(); +} + +Vector<int> RasterizerStorageGLES3::voxel_gi_get_level_counts(RID p_voxel_gi) const { + return Vector<int>(); +} + +Transform3D RasterizerStorageGLES3::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const { + return Transform3D(); +} + +void RasterizerStorageGLES3::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) { +} + +float RasterizerStorageGLES3::voxel_gi_get_dynamic_range(RID p_voxel_gi) const { + return 0; +} + +void RasterizerStorageGLES3::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) { +} + +float RasterizerStorageGLES3::voxel_gi_get_propagation(RID p_voxel_gi) const { + return 0; +} + +void RasterizerStorageGLES3::voxel_gi_set_energy(RID p_voxel_gi, float p_range) { +} + +float RasterizerStorageGLES3::voxel_gi_get_energy(RID p_voxel_gi) const { + return 0.0; +} + +void RasterizerStorageGLES3::voxel_gi_set_bias(RID p_voxel_gi, float p_range) { +} + +float RasterizerStorageGLES3::voxel_gi_get_bias(RID p_voxel_gi) const { + return 0.0; +} + +void RasterizerStorageGLES3::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) { +} + +float RasterizerStorageGLES3::voxel_gi_get_normal_bias(RID p_voxel_gi) const { + return 0.0; +} + +void RasterizerStorageGLES3::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) { +} + +bool RasterizerStorageGLES3::voxel_gi_is_interior(RID p_voxel_gi) const { + return false; +} + +void RasterizerStorageGLES3::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) { +} + +bool RasterizerStorageGLES3::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const { + return false; +} + +void RasterizerStorageGLES3::voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) { +} + +float RasterizerStorageGLES3::voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const { + return 0; +} + +uint32_t RasterizerStorageGLES3::voxel_gi_get_version(RID p_voxel_gi) { + return 0; +} + +/* LIGHTMAP CAPTURE */ +RID RasterizerStorageGLES3::lightmap_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::lightmap_initialize(RID p_rid) { +} + +void RasterizerStorageGLES3::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { +} + +void RasterizerStorageGLES3::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) { +} + +void RasterizerStorageGLES3::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) { +} + +void RasterizerStorageGLES3::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { +} + +PackedVector3Array RasterizerStorageGLES3::lightmap_get_probe_capture_points(RID p_lightmap) const { + return PackedVector3Array(); +} + +PackedColorArray RasterizerStorageGLES3::lightmap_get_probe_capture_sh(RID p_lightmap) const { + return PackedColorArray(); +} + +PackedInt32Array RasterizerStorageGLES3::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const { + return PackedInt32Array(); +} + +PackedInt32Array RasterizerStorageGLES3::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const { + return PackedInt32Array(); +} + +AABB RasterizerStorageGLES3::lightmap_get_aabb(RID p_lightmap) const { + return AABB(); +} + +void RasterizerStorageGLES3::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) { +} + +bool RasterizerStorageGLES3::lightmap_is_interior(RID p_lightmap) const { + return false; +} + +void RasterizerStorageGLES3::lightmap_set_probe_capture_update_speed(float p_speed) { +} + +float RasterizerStorageGLES3::lightmap_get_probe_capture_update_speed() const { + return 0; +} + +/* OCCLUDER */ + +void RasterizerStorageGLES3::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { +} + +/* PARTICLES */ + +RID RasterizerStorageGLES3::particles_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::particles_initialize(RID p_rid) { +} + +void RasterizerStorageGLES3::particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) { +} + +void RasterizerStorageGLES3::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) { +} + +void RasterizerStorageGLES3::particles_set_emitting(RID p_particles, bool p_emitting) { +} + +void RasterizerStorageGLES3::particles_set_amount(RID p_particles, int p_amount) { +} + +void RasterizerStorageGLES3::particles_set_lifetime(RID p_particles, double p_lifetime) { +} + +void RasterizerStorageGLES3::particles_set_one_shot(RID p_particles, bool p_one_shot) { +} + +void RasterizerStorageGLES3::particles_set_pre_process_time(RID p_particles, double p_time) { +} + +void RasterizerStorageGLES3::particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) { +} + +void RasterizerStorageGLES3::particles_set_randomness_ratio(RID p_particles, real_t p_ratio) { +} + +void RasterizerStorageGLES3::particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) { +} + +void RasterizerStorageGLES3::particles_set_speed_scale(RID p_particles, double p_scale) { +} + +void RasterizerStorageGLES3::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { +} + +void RasterizerStorageGLES3::particles_set_process_material(RID p_particles, RID p_material) { +} +RID RasterizerStorageGLES3::particles_get_process_material(RID p_particles) const { + return RID(); +} + +void RasterizerStorageGLES3::particles_set_fixed_fps(RID p_particles, int p_fps) { +} + +void RasterizerStorageGLES3::particles_set_interpolate(RID p_particles, bool p_enable) { +} + +void RasterizerStorageGLES3::particles_set_fractional_delta(RID p_particles, bool p_enable) { +} + +void RasterizerStorageGLES3::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { +} + +void RasterizerStorageGLES3::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { +} + +void RasterizerStorageGLES3::particles_set_collision_base_size(RID p_particles, real_t p_size) { +} + +void RasterizerStorageGLES3::particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) { +} + +void RasterizerStorageGLES3::particles_set_trails(RID p_particles, bool p_enable, double p_length) { +} + +void RasterizerStorageGLES3::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) { +} + +void RasterizerStorageGLES3::particles_restart(RID p_particles) { +} + +void RasterizerStorageGLES3::particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) { +} + +void RasterizerStorageGLES3::particles_set_draw_passes(RID p_particles, int p_count) { +} + +void RasterizerStorageGLES3::particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) { +} + +void RasterizerStorageGLES3::particles_request_process(RID p_particles) { +} + +AABB RasterizerStorageGLES3::particles_get_current_aabb(RID p_particles) { + return AABB(); +} + +AABB RasterizerStorageGLES3::particles_get_aabb(RID p_particles) const { + return AABB(); +} + +void RasterizerStorageGLES3::particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) { +} + +bool RasterizerStorageGLES3::particles_get_emitting(RID p_particles) { + return false; +} + +int RasterizerStorageGLES3::particles_get_draw_passes(RID p_particles) const { + return 0; +} + +RID RasterizerStorageGLES3::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { + return RID(); +} + +void RasterizerStorageGLES3::particles_add_collision(RID p_particles, RID p_instance) { +} + +void RasterizerStorageGLES3::particles_remove_collision(RID p_particles, RID p_instance) { +} + +void RasterizerStorageGLES3::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) { +} + +void RasterizerStorageGLES3::update_particles() { +} + +/* PARTICLES COLLISION */ + +RID RasterizerStorageGLES3::particles_collision_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::particles_collision_initialize(RID p_rid) { +} + +void RasterizerStorageGLES3::particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) { +} + +void RasterizerStorageGLES3::particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) { +} + +void RasterizerStorageGLES3::particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) { +} + +void RasterizerStorageGLES3::particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) { +} + +void RasterizerStorageGLES3::particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) { +} + +void RasterizerStorageGLES3::particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) { +} + +void RasterizerStorageGLES3::particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) { +} + +void RasterizerStorageGLES3::particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) { +} + +void RasterizerStorageGLES3::particles_collision_height_field_update(RID p_particles_collision) { +} + +void RasterizerStorageGLES3::particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) { +} + +AABB RasterizerStorageGLES3::particles_collision_get_aabb(RID p_particles_collision) const { + return AABB(); +} + +bool RasterizerStorageGLES3::particles_collision_is_heightfield(RID p_particles_collision) const { + return false; +} + +RID RasterizerStorageGLES3::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const { + return RID(); +} + +RID RasterizerStorageGLES3::particles_collision_instance_create(RID p_collision) { + return RID(); +} + +void RasterizerStorageGLES3::particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) { +} + +void RasterizerStorageGLES3::particles_collision_instance_set_active(RID p_collision_instance, bool p_active) { +} + +RID RasterizerStorageGLES3::fog_volume_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::fog_volume_initialize(RID p_rid) { +} + +void RasterizerStorageGLES3::fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) { +} + +void RasterizerStorageGLES3::fog_volume_set_extents(RID p_fog_volume, const Vector3 &p_extents) { +} + +void RasterizerStorageGLES3::fog_volume_set_material(RID p_fog_volume, RID p_material) { +} + +AABB RasterizerStorageGLES3::fog_volume_get_aabb(RID p_fog_volume) const { + return AABB(); +} + +RS::FogVolumeShape RasterizerStorageGLES3::fog_volume_get_shape(RID p_fog_volume) const { + return RS::FOG_VOLUME_SHAPE_BOX; +} + +/* VISIBILITY NOTIFIER */ +RID RasterizerStorageGLES3::visibility_notifier_allocate() { + return RID(); +} + +void RasterizerStorageGLES3::visibility_notifier_initialize(RID p_notifier) { +} + +void RasterizerStorageGLES3::visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) { +} + +void RasterizerStorageGLES3::visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) { +} + +AABB RasterizerStorageGLES3::visibility_notifier_get_aabb(RID p_notifier) const { + return AABB(); +} + +void RasterizerStorageGLES3::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) { +} + +/* GLOBAL VARIABLES */ + +void RasterizerStorageGLES3::global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) { +} + +void RasterizerStorageGLES3::global_variable_remove(const StringName &p_name) { +} + +Vector<StringName> RasterizerStorageGLES3::global_variable_get_list() const { + return Vector<StringName>(); +} + +void RasterizerStorageGLES3::global_variable_set(const StringName &p_name, const Variant &p_value) { +} + +void RasterizerStorageGLES3::global_variable_set_override(const StringName &p_name, const Variant &p_value) { +} + +Variant RasterizerStorageGLES3::global_variable_get(const StringName &p_name) const { + return Variant(); +} + +RS::GlobalVariableType RasterizerStorageGLES3::global_variable_get_type(const StringName &p_name) const { + return RS::GLOBAL_VAR_TYPE_MAX; +} + +void RasterizerStorageGLES3::global_variables_load_settings(bool p_load_textures) { +} + +void RasterizerStorageGLES3::global_variables_clear() { +} + +int32_t RasterizerStorageGLES3::global_variables_instance_allocate(RID p_instance) { + return 0; +} + +void RasterizerStorageGLES3::global_variables_instance_free(RID p_instance) { +} + +void RasterizerStorageGLES3::global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) { +} + +bool RasterizerStorageGLES3::particles_is_inactive(RID p_particles) const { + return false; +} + +/* RENDER TARGET */ + +void RasterizerStorageGLES3::_set_current_render_target(RID p_render_target) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + + if (rt) { + if (rt->allocate_is_dirty) { + rt->allocate_is_dirty = false; + _render_target_allocate(rt); + } + + frame.current_rt = rt; + ERR_FAIL_COND(!rt); + frame.clear_request = false; + + glViewport(0, 0, rt->width, rt->height); + + _dims.rt_width = rt->width; + _dims.rt_height = rt->height; + _dims.win_width = rt->width; + _dims.win_height = rt->height; + + } else { + frame.current_rt = nullptr; + frame.clear_request = false; + bind_framebuffer_system(); + } +} + +void RasterizerStorageGLES3::_render_target_allocate(GLES3::RenderTarget *rt) { + // do not allocate a render target with no size + if (rt->width <= 0 || rt->height <= 0) { + return; + } + + // do not allocate a render target that is attached to the screen + if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) { + rt->fbo = RasterizerStorageGLES3::system_fbo; + return; + } + + GLuint color_internal_format; + GLuint color_format; + GLuint color_type = GL_UNSIGNED_BYTE; + Image::Format image_format; + + if (rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { +#ifdef GLES_OVER_GL + color_internal_format = GL_RGBA8; +#else + color_internal_format = GL_RGBA; +#endif + color_format = GL_RGBA; + image_format = Image::FORMAT_RGBA8; + } else { +#ifdef GLES_OVER_GL + color_internal_format = GL_RGB8; +#else + color_internal_format = GL_RGB; +#endif + color_format = GL_RGB; + image_format = Image::FORMAT_RGB8; + } + + rt->used_dof_blur_near = false; + rt->mip_maps_allocated = false; + + { + /* Front FBO */ + + GLES3::Texture *texture = GLES3::TextureStorage::get_singleton()->get_texture(rt->texture); + ERR_FAIL_COND(!texture); + + // framebuffer + glGenFramebuffers(1, &rt->fbo); + bind_framebuffer(rt->fbo); + + // color + glGenTextures(1, &rt->color); + glBindTexture(GL_TEXTURE_2D, rt->color); + + glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, rt->width, rt->height, 0, color_format, color_type, nullptr); + + if (texture->flags & GLES3::TEXTURE_FLAG_FILTER) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + + // depth + + if (config->support_depth_texture) { + glGenTextures(1, &rt->depth); + glBindTexture(GL_TEXTURE_2D, rt->depth); + glTexImage2D(GL_TEXTURE_2D, 0, config->depth_internalformat, rt->width, rt->height, 0, GL_DEPTH_COMPONENT, config->depth_type, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + } else { + glGenRenderbuffers(1, &rt->depth); + glBindRenderbuffer(GL_RENDERBUFFER, rt->depth); + + glRenderbufferStorage(GL_RENDERBUFFER, config->depth_buffer_internalformat, rt->width, rt->height); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + glDeleteFramebuffers(1, &rt->fbo); + if (config->support_depth_texture) { + glDeleteTextures(1, &rt->depth); + } else { + glDeleteRenderbuffers(1, &rt->depth); + } + + glDeleteTextures(1, &rt->color); + rt->fbo = 0; + rt->width = 0; + rt->height = 0; + rt->color = 0; + rt->depth = 0; + texture->tex_id = 0; + texture->active = false; + WARN_PRINT("Could not create framebuffer!!"); + return; + } + + texture->format = image_format; + texture->gl_format_cache = color_format; + texture->gl_type_cache = GL_UNSIGNED_BYTE; + texture->gl_internal_format_cache = color_internal_format; + texture->tex_id = rt->color; + texture->width = rt->width; + texture->alloc_width = rt->width; + texture->height = rt->height; + texture->alloc_height = rt->height; + texture->active = true; + + GLES3::TextureStorage::get_singleton()->texture_set_flags(rt->texture, texture->flags); + } + + /* BACK FBO */ + /* For MSAA */ + +#ifndef JAVASCRIPT_ENABLED + if (rt->msaa >= RS::VIEWPORT_MSAA_2X && rt->msaa <= RS::VIEWPORT_MSAA_8X) { + rt->multisample_active = true; + + static const int msaa_value[] = { 0, 2, 4, 8, 16 }; + int msaa = msaa_value[rt->msaa]; + + int max_samples = 0; + glGetIntegerv(GL_MAX_SAMPLES, &max_samples); + if (msaa > max_samples) { + WARN_PRINT("MSAA must be <= GL_MAX_SAMPLES, falling-back to GL_MAX_SAMPLES = " + itos(max_samples)); + msaa = max_samples; + } + + //regular fbo + glGenFramebuffers(1, &rt->multisample_fbo); + bind_framebuffer(rt->multisample_fbo); + + glGenRenderbuffers(1, &rt->multisample_depth); + glBindRenderbuffer(GL_RENDERBUFFER, rt->multisample_depth); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, config->depth_buffer_internalformat, rt->width, rt->height); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->multisample_depth); + + glGenRenderbuffers(1, &rt->multisample_color); + glBindRenderbuffer(GL_RENDERBUFFER, rt->multisample_color); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, color_internal_format, rt->width, rt->height); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rt->multisample_color); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + // Delete allocated resources and default to no MSAA + WARN_PRINT_ONCE("Cannot allocate back framebuffer for MSAA"); + printf("err status: %x\n", status); + rt->multisample_active = false; + + glDeleteFramebuffers(1, &rt->multisample_fbo); + rt->multisample_fbo = 0; + + glDeleteRenderbuffers(1, &rt->multisample_depth); + rt->multisample_depth = 0; + + glDeleteRenderbuffers(1, &rt->multisample_color); + rt->multisample_color = 0; + } + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + bind_framebuffer(0); + + } else +#endif // JAVASCRIPT_ENABLED + { + rt->multisample_active = false; + } + + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // copy texscreen buffers + // if (!(rt->flags[RendererStorage::RENDER_TARGET_NO_SAMPLING])) { + if (true) { + glGenTextures(1, &rt->copy_screen_effect.color); + glBindTexture(GL_TEXTURE_2D, rt->copy_screen_effect.color); + + if (rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rt->width, rt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, rt->width, rt->height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glGenFramebuffers(1, &rt->copy_screen_effect.fbo); + bind_framebuffer(rt->copy_screen_effect.fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->copy_screen_effect.color, 0); + + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _render_target_clear(rt); + ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); + } + } + + // Allocate mipmap chains for post_process effects + // if (!rt->flags[RendererStorage::RENDER_TARGET_NO_3D] && rt->width >= 2 && rt->height >= 2) { + if (rt->width >= 2 && rt->height >= 2) { + for (int i = 0; i < 2; i++) { + ERR_FAIL_COND(rt->mip_maps[i].sizes.size()); + int w = rt->width; + int h = rt->height; + + if (i > 0) { + w >>= 1; + h >>= 1; + } + + int level = 0; + int fb_w = w; + int fb_h = h; + + while (true) { + GLES3::RenderTarget::MipMaps::Size mm; + mm.width = w; + mm.height = h; + rt->mip_maps[i].sizes.push_back(mm); + + w >>= 1; + h >>= 1; + + if (w < 2 || h < 2) { + break; + } + + level++; + } + + GLsizei width = fb_w; + GLsizei height = fb_h; + + if (config->render_to_mipmap_supported) { + glGenTextures(1, &rt->mip_maps[i].color); + glBindTexture(GL_TEXTURE_2D, rt->mip_maps[i].color); + + for (int l = 0; l < level + 1; l++) { + glTexImage2D(GL_TEXTURE_2D, l, color_internal_format, width, height, 0, color_format, color_type, nullptr); + width = MAX(1, (width / 2)); + height = MAX(1, (height / 2)); + } +#ifdef GLES_OVER_GL + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level); +#endif + } else { + // Can't render to specific levels of a mipmap in ES 2.0 or Webgl so create a texture for each level + for (int l = 0; l < level + 1; l++) { + glGenTextures(1, &rt->mip_maps[i].sizes.write[l].color); + glBindTexture(GL_TEXTURE_2D, rt->mip_maps[i].sizes[l].color); + glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, width, height, 0, color_format, color_type, nullptr); + width = MAX(1, (width / 2)); + height = MAX(1, (height / 2)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + } + + glDisable(GL_SCISSOR_TEST); + glColorMask(1, 1, 1, 1); + glDepthMask(GL_TRUE); + + for (int j = 0; j < rt->mip_maps[i].sizes.size(); j++) { + GLES3::RenderTarget::MipMaps::Size &mm = rt->mip_maps[i].sizes.write[j]; + + glGenFramebuffers(1, &mm.fbo); + bind_framebuffer(mm.fbo); + + if (config->render_to_mipmap_supported) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->mip_maps[i].color, j); + } else { + glBindTexture(GL_TEXTURE_2D, rt->mip_maps[i].sizes[j].color); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->mip_maps[i].sizes[j].color, 0); + } + + bool used_depth = false; + if (j == 0 && i == 0) { //use always + if (config->support_depth_texture) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + } else { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); + } + used_depth = true; + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT_ONCE("Cannot allocate mipmaps for 3D post processing effects"); + bind_framebuffer_system(); + return; + } + + glClearColor(1.0, 0.0, 1.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + if (used_depth) { + glClearDepth(1.0); + glClear(GL_DEPTH_BUFFER_BIT); + } + } + + rt->mip_maps[i].levels = level; + + if (config->render_to_mipmap_supported) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + } + rt->mip_maps_allocated = true; + } + + bind_framebuffer_system(); +} + +void RasterizerStorageGLES3::_render_target_clear(GLES3::RenderTarget *rt) { + // there is nothing to clear when DIRECT_TO_SCREEN is used + if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) { + return; + } + + if (rt->fbo) { + glDeleteFramebuffers(1, &rt->fbo); + glDeleteTextures(1, &rt->color); + rt->fbo = 0; + } + + if (rt->external.fbo != 0) { + // free this + glDeleteFramebuffers(1, &rt->external.fbo); + + // clean up our texture + GLES3::Texture *t = GLES3::TextureStorage::get_singleton()->get_texture(rt->external.texture); + t->alloc_height = 0; + t->alloc_width = 0; + t->width = 0; + t->height = 0; + t->active = false; + GLES3::TextureStorage::get_singleton()->texture_free(rt->external.texture); + memdelete(t); + + rt->external.fbo = 0; + } + + if (rt->depth) { + if (config->support_depth_texture) { + glDeleteTextures(1, &rt->depth); + } else { + glDeleteRenderbuffers(1, &rt->depth); + } + + rt->depth = 0; + } + + GLES3::Texture *tex = GLES3::TextureStorage::get_singleton()->get_texture(rt->texture); + tex->alloc_height = 0; + tex->alloc_width = 0; + tex->width = 0; + tex->height = 0; + tex->active = false; + + if (rt->copy_screen_effect.color) { + glDeleteFramebuffers(1, &rt->copy_screen_effect.fbo); + rt->copy_screen_effect.fbo = 0; + + glDeleteTextures(1, &rt->copy_screen_effect.color); + rt->copy_screen_effect.color = 0; + } + + for (int i = 0; i < 2; i++) { + if (rt->mip_maps[i].sizes.size()) { + for (int j = 0; j < rt->mip_maps[i].sizes.size(); j++) { + glDeleteFramebuffers(1, &rt->mip_maps[i].sizes[j].fbo); + glDeleteTextures(1, &rt->mip_maps[i].sizes[j].color); + } + + glDeleteTextures(1, &rt->mip_maps[i].color); + rt->mip_maps[i].sizes.clear(); + rt->mip_maps[i].levels = 0; + rt->mip_maps[i].color = 0; + } + } + + if (rt->multisample_active) { + glDeleteFramebuffers(1, &rt->multisample_fbo); + rt->multisample_fbo = 0; + + glDeleteRenderbuffers(1, &rt->multisample_depth); + rt->multisample_depth = 0; + + glDeleteRenderbuffers(1, &rt->multisample_color); + + rt->multisample_color = 0; + } +} + +RID RasterizerStorageGLES3::render_target_create() { + GLES3::RenderTarget *rt = memnew(GLES3::RenderTarget); + GLES3::Texture *t = memnew(GLES3::Texture); + + t->type = RenderingDevice::TEXTURE_TYPE_2D; + t->flags = 0; + t->width = 0; + t->height = 0; + t->alloc_height = 0; + t->alloc_width = 0; + t->format = Image::FORMAT_R8; + t->target = GL_TEXTURE_2D; + t->gl_format_cache = 0; + t->gl_internal_format_cache = 0; + t->gl_type_cache = 0; + t->data_size = 0; + t->total_data_size = 0; + t->ignore_mipmaps = false; + t->compressed = false; + t->mipmaps = 1; + t->active = true; + t->tex_id = 0; + t->render_target = rt; + + rt->texture = GLES3::TextureStorage::get_singleton()->make_rid(t); + return render_target_owner.make_rid(rt); +} + +void RasterizerStorageGLES3::render_target_set_position(RID p_render_target, int p_x, int p_y) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + rt->x = p_x; + rt->y = p_y; +} + +void RasterizerStorageGLES3::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + if (p_width == rt->width && p_height == rt->height) { + return; + } + + _render_target_clear(rt); + + rt->width = p_width; + rt->height = p_height; + + // print_line("render_target_set_size " + itos(p_render_target.get_id()) + ", w " + itos(p_width) + " h " + itos(p_height)); + + rt->allocate_is_dirty = true; + //_render_target_allocate(rt); +} + +// TODO: convert to Size2i internally +Size2i RasterizerStorageGLES3::render_target_get_size(RID p_render_target) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, Size2()); + + return Size2i(rt->width, rt->height); +} + +RID RasterizerStorageGLES3::render_target_get_texture(RID p_render_target) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + if (rt->external.fbo == 0) { + return rt->texture; + } else { + return rt->external.texture; + } +} + +void RasterizerStorageGLES3::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + if (p_texture_id == 0) { + if (rt->external.fbo != 0) { + // free this + glDeleteFramebuffers(1, &rt->external.fbo); + + // and this + if (rt->external.depth != 0) { + glDeleteRenderbuffers(1, &rt->external.depth); + } + + // clean up our texture + GLES3::Texture *t = GLES3::TextureStorage::get_singleton()->get_texture(rt->external.texture); + t->alloc_height = 0; + t->alloc_width = 0; + t->width = 0; + t->height = 0; + t->active = false; + GLES3::TextureStorage::get_singleton()->texture_free(rt->external.texture); + memdelete(t); + + rt->external.fbo = 0; + rt->external.color = 0; + rt->external.depth = 0; + } + } else { + GLES3::Texture *t; + + if (rt->external.fbo == 0) { + // create our fbo + glGenFramebuffers(1, &rt->external.fbo); + bind_framebuffer(rt->external.fbo); + + // allocate a texture + t = memnew(GLES3::Texture); + + t->type = RenderingDevice::TEXTURE_TYPE_2D; + t->flags = 0; + t->width = 0; + t->height = 0; + t->alloc_height = 0; + t->alloc_width = 0; + t->format = Image::FORMAT_RGBA8; + t->target = GL_TEXTURE_2D; + t->gl_format_cache = 0; + t->gl_internal_format_cache = 0; + t->gl_type_cache = 0; + t->data_size = 0; + t->compressed = false; + t->srgb = false; + t->total_data_size = 0; + t->ignore_mipmaps = false; + t->mipmaps = 1; + t->active = true; + t->tex_id = 0; + t->render_target = rt; + + rt->external.texture = GLES3::TextureStorage::get_singleton()->make_rid(t); + + } else { + // bind our frame buffer + bind_framebuffer(rt->external.fbo); + + // find our texture + t = GLES3::TextureStorage::get_singleton()->get_texture(rt->external.texture); + } + + // set our texture + t->tex_id = p_texture_id; + rt->external.color = p_texture_id; + + // size shouldn't be different + t->width = rt->width; + t->height = rt->height; + t->alloc_height = rt->width; + t->alloc_width = rt->height; + + // Switch our texture on our frame buffer + { + // set our texture as the destination for our framebuffer + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_texture_id, 0); + + // seeing we're rendering into this directly, better also use our depth buffer, just use our existing one :) + if (config->support_depth_texture) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + } else { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); + } + } + + // check status and unbind + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + bind_framebuffer_system(); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + printf("framebuffer fail, status: %x\n", status); + } + + ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); + } +} + +void RasterizerStorageGLES3::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + // When setting DIRECT_TO_SCREEN, you need to clear before the value is set, but allocate after as + // those functions change how they operate depending on the value of DIRECT_TO_SCREEN + if (p_flag == RENDER_TARGET_DIRECT_TO_SCREEN && p_value != rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) { + _render_target_clear(rt); + rt->flags[p_flag] = p_value; + _render_target_allocate(rt); + } + + rt->flags[p_flag] = p_value; + + switch (p_flag) { + case RENDER_TARGET_TRANSPARENT: + /* + case RENDER_TARGET_HDR: + case RENDER_TARGET_NO_3D: + case RENDER_TARGET_NO_SAMPLING: + case RENDER_TARGET_NO_3D_EFFECTS: */ + { + //must reset for these formats + _render_target_clear(rt); + _render_target_allocate(rt); + } + break; + default: { + } + } +} + +bool RasterizerStorageGLES3::render_target_was_used(RID p_render_target) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, false); + + return rt->used_in_frame; +} + +void RasterizerStorageGLES3::render_target_clear_used(RID p_render_target) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + rt->used_in_frame = false; +} + +void RasterizerStorageGLES3::render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + if (rt->msaa == p_msaa) { + return; + } + + _render_target_clear(rt); + rt->msaa = p_msaa; + _render_target_allocate(rt); +} + +//RasterizerStorageGLES3::GLES3::RenderTarget * RasterizerStorageGLES3::render_target_get(RID p_render_target) +//{ +// return render_target_owner.get_or_null(p_render_target); +//} + +void RasterizerStorageGLES3::render_target_set_use_fxaa(RID p_render_target, bool p_fxaa) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + rt->use_fxaa = p_fxaa; +} + +void RasterizerStorageGLES3::render_target_set_use_debanding(RID p_render_target, bool p_debanding) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + if (p_debanding) { + WARN_PRINT_ONCE("Debanding is not supported in the OpenGL backend. Switch to the Vulkan backend and make sure HDR is enabled."); + } + + rt->use_debanding = p_debanding; +} + +void RasterizerStorageGLES3::render_target_request_clear(RID p_render_target, const Color &p_clear_color) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + rt->clear_requested = true; + rt->clear_color = p_clear_color; + + // ERR_FAIL_COND(!frame.current_rt); + // frame.clear_request = true; + // frame.clear_request_color = p_color; +} + +bool RasterizerStorageGLES3::render_target_is_clear_requested(RID p_render_target) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, false); + return rt->clear_requested; +} +Color RasterizerStorageGLES3::render_target_get_clear_request_color(RID p_render_target) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, Color()); + return rt->clear_color; +} + +void RasterizerStorageGLES3::render_target_disable_clear_request(RID p_render_target) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + rt->clear_requested = false; +} + +void RasterizerStorageGLES3::render_target_do_clear_request(RID p_render_target) { +} + +void RasterizerStorageGLES3::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { +} + +Rect2i RasterizerStorageGLES3::render_target_get_sdf_rect(RID p_render_target) const { + return Rect2i(); +} + +void RasterizerStorageGLES3::render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) { +} + +/* CANVAS SHADOW */ + +RID RasterizerStorageGLES3::canvas_light_shadow_buffer_create(int p_width) { + CanvasLightShadow *cls = memnew(CanvasLightShadow); + + if (p_width > config->max_texture_size) { + p_width = config->max_texture_size; + } + + cls->size = p_width; + cls->height = 16; + + glActiveTexture(GL_TEXTURE0); + + glGenFramebuffers(1, &cls->fbo); + bind_framebuffer(cls->fbo); + + glGenRenderbuffers(1, &cls->depth); + glBindRenderbuffer(GL_RENDERBUFFER, cls->depth); + glRenderbufferStorage(GL_RENDERBUFFER, config->depth_buffer_internalformat, cls->size, cls->height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, cls->depth); + + glGenTextures(1, &cls->distance); + glBindTexture(GL_TEXTURE_2D, cls->distance); + if (config->use_rgba_2d_shadows) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cls->size, cls->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + } else { +#ifdef GLES_OVER_GL + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cls->size, cls->height, 0, _RED_OES, GL_FLOAT, nullptr); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_FLOAT, cls->size, cls->height, 0, _RED_OES, GL_FLOAT, NULL); +#endif + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cls->distance, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + //printf("errnum: %x\n",status); + bind_framebuffer_system(); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + memdelete(cls); + ERR_FAIL_COND_V(status != GL_FRAMEBUFFER_COMPLETE, RID()); + } + + return canvas_light_shadow_owner.make_rid(cls); +} + +/* LIGHT SHADOW MAPPING */ +/* + +RID RasterizerStorageGLES3::canvas_light_occluder_create() { + CanvasOccluder *co = memnew(CanvasOccluder); + co->index_id = 0; + co->vertex_id = 0; + co->len = 0; + + return canvas_occluder_owner.make_rid(co); +} + +void RasterizerStorageGLES3::canvas_light_occluder_set_polylines(RID p_occluder, const PoolVector<Vector2> &p_lines) { + CanvasOccluder *co = canvas_occluder_owner.get(p_occluder); + ERR_FAIL_COND(!co); + + co->lines = p_lines; + + if (p_lines.size() != co->len) { + if (co->index_id) { + glDeleteBuffers(1, &co->index_id); + } if (co->vertex_id) { + glDeleteBuffers(1, &co->vertex_id); + } + + co->index_id = 0; + co->vertex_id = 0; + co->len = 0; + } + + if (p_lines.size()) { + PoolVector<float> geometry; + PoolVector<uint16_t> indices; + int lc = p_lines.size(); + + geometry.resize(lc * 6); + indices.resize(lc * 3); + + PoolVector<float>::Write vw = geometry.write(); + PoolVector<uint16_t>::Write iw = indices.write(); + + PoolVector<Vector2>::Read lr = p_lines.read(); + + const int POLY_HEIGHT = 16384; + + for (int i = 0; i < lc / 2; i++) { + vw[i * 12 + 0] = lr[i * 2 + 0].x; + vw[i * 12 + 1] = lr[i * 2 + 0].y; + vw[i * 12 + 2] = POLY_HEIGHT; + + vw[i * 12 + 3] = lr[i * 2 + 1].x; + vw[i * 12 + 4] = lr[i * 2 + 1].y; + vw[i * 12 + 5] = POLY_HEIGHT; + + vw[i * 12 + 6] = lr[i * 2 + 1].x; + vw[i * 12 + 7] = lr[i * 2 + 1].y; + vw[i * 12 + 8] = -POLY_HEIGHT; + + vw[i * 12 + 9] = lr[i * 2 + 0].x; + vw[i * 12 + 10] = lr[i * 2 + 0].y; + vw[i * 12 + 11] = -POLY_HEIGHT; + + iw[i * 6 + 0] = i * 4 + 0; + iw[i * 6 + 1] = i * 4 + 1; + iw[i * 6 + 2] = i * 4 + 2; + + iw[i * 6 + 3] = i * 4 + 2; + iw[i * 6 + 4] = i * 4 + 3; + iw[i * 6 + 5] = i * 4 + 0; + } + + //if same buffer len is being set, just use BufferSubData to avoid a pipeline flush + + if (!co->vertex_id) { + glGenBuffers(1, &co->vertex_id); + glBindBuffer(GL_ARRAY_BUFFER, co->vertex_id); + glBufferData(GL_ARRAY_BUFFER, lc * 6 * sizeof(real_t), vw.ptr(), GL_STATIC_DRAW); + } else { + glBindBuffer(GL_ARRAY_BUFFER, co->vertex_id); + glBufferSubData(GL_ARRAY_BUFFER, 0, lc * 6 * sizeof(real_t), vw.ptr()); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + if (!co->index_id) { + glGenBuffers(1, &co->index_id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, co->index_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, lc * 3 * sizeof(uint16_t), iw.ptr(), GL_DYNAMIC_DRAW); + } else { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, co->index_id); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, lc * 3 * sizeof(uint16_t), iw.ptr()); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind + + co->len = lc; + } +} +*/ + +RS::InstanceType RasterizerStorageGLES3::get_base_type(RID p_rid) const { + return RS::INSTANCE_NONE; + + /* + if (mesh_owner.owns(p_rid)) { + return RS::INSTANCE_MESH; + } else if (light_owner.owns(p_rid)) { + return RS::INSTANCE_LIGHT; + } else if (multimesh_owner.owns(p_rid)) { + return RS::INSTANCE_MULTIMESH; + } else if (immediate_owner.owns(p_rid)) { + return RS::INSTANCE_IMMEDIATE; + } else if (reflection_probe_owner.owns(p_rid)) { + return RS::INSTANCE_REFLECTION_PROBE; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + return RS::INSTANCE_LIGHTMAP_CAPTURE; + } else { + return RS::INSTANCE_NONE; + } +*/ +} + +bool RasterizerStorageGLES3::free(RID p_rid) { + if (render_target_owner.owns(p_rid)) { + GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_rid); + _render_target_clear(rt); + + GLES3::Texture *t = GLES3::TextureStorage::get_singleton()->get_texture(rt->texture); + if (t) { + GLES3::TextureStorage::get_singleton()->texture_free(rt->texture); + memdelete(t); + } + render_target_owner.free(p_rid); + memdelete(rt); + + return true; + } else if (GLES3::TextureStorage::get_singleton()->owns_texture(p_rid)) { + GLES3::TextureStorage::get_singleton()->texture_free(p_rid); + return true; + } else if (GLES3::CanvasTextureStorage::get_singleton()->owns_canvas_texture(p_rid)) { + GLES3::CanvasTextureStorage::get_singleton()->canvas_texture_free(p_rid); + return true; + } else if (sky_owner.owns(p_rid)) { + Sky *sky = sky_owner.get_or_null(p_rid); + sky_set_texture(p_rid, RID(), 256); + sky_owner.free(p_rid); + memdelete(sky); + + return true; + } else if (shader_owner.owns(p_rid)) { + Shader *shader = shader_owner.get_or_null(p_rid); + + if (shader->shader && shader->version.is_valid()) { + shader->shader->version_free(shader->version); + } + + if (shader->dirty_list.in_list()) { + _shader_dirty_list.remove(&shader->dirty_list); + } + + while (shader->materials.first()) { + Material *m = shader->materials.first()->self(); + + m->shader = nullptr; + _material_make_dirty(m); + + shader->materials.remove(shader->materials.first()); + } + + shader_owner.free(p_rid); + memdelete(shader); + + return true; + } else if (material_owner.owns(p_rid)) { + Material *m = material_owner.get_or_null(p_rid); + + if (m->shader) { + m->shader->materials.remove(&m->list); + } + + /* + for (Map<Geometry *, int>::Element *E = m->geometry_owners.front(); E; E = E->next()) { + Geometry *g = E->key(); + g->material = RID(); + } + + for (Map<InstanceBaseDependency *, int>::Element *E = m->instance_owners.front(); E; E = E->next()) { + InstanceBaseDependency *ins = E->key(); + + if (ins->material_override == p_rid) { + ins->material_override = RID(); + } + + for (int i = 0; i < ins->materials.size(); i++) { + if (ins->materials[i] == p_rid) { + ins->materials.write[i] = RID(); + } + } + } +*/ + + material_owner.free(p_rid); + memdelete(m); + + return true; + + } else { + return false; + } + /* + } else if (skeleton_owner.owns(p_rid)) { + Skeleton *s = skeleton_owner.get_or_null(p_rid); + + if (s->update_list.in_list()) { + skeleton_update_list.remove(&s->update_list); + } + + for (Set<InstanceBaseDependency *>::Element *E = s->instances.front(); E; E = E->next()) { + E->get()->skeleton = RID(); + } + + skeleton_allocate(p_rid, 0, false); + + if (s->tex_id) { + glDeleteTextures(1, &s->tex_id); + } + + skeleton_owner.free(p_rid); + memdelete(s); + + return true; + } else if (mesh_owner.owns(p_rid)) { + Mesh *mesh = mesh_owner.get_or_null(p_rid); + + mesh->instance_remove_deps(); + mesh_clear(p_rid); + + while (mesh->multimeshes.first()) { + MultiMesh *multimesh = mesh->multimeshes.first()->self(); + multimesh->mesh = RID(); + multimesh->dirty_aabb = true; + + mesh->multimeshes.remove(mesh->multimeshes.first()); + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } + } + + mesh_owner.free(p_rid); + memdelete(mesh); + + return true; + } else if (multimesh_owner.owns(p_rid)) { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid); + multimesh->instance_remove_deps(); + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.get_or_null(multimesh->mesh); + if (mesh) { + mesh->multimeshes.remove(&multimesh->mesh_list); + } + } + + multimesh_allocate(p_rid, 0, RS::MULTIMESH_TRANSFORM_3D, RS::MULTIMESH_COLOR_NONE); + + update_dirty_multimeshes(); + + multimesh_owner.free(p_rid); + memdelete(multimesh); + + return true; + } else if (immediate_owner.owns(p_rid)) { + Immediate *im = immediate_owner.get_or_null(p_rid); + im->instance_remove_deps(); + + immediate_owner.free(p_rid); + memdelete(im); + + return true; + } else if (light_owner.owns(p_rid)) { + Light *light = light_owner.get_or_null(p_rid); + light->instance_remove_deps(); + + light_owner.free(p_rid); + memdelete(light); + + return true; + } else if (reflection_probe_owner.owns(p_rid)) { + // delete the texture + ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_rid); + reflection_probe->instance_remove_deps(); + + reflection_probe_owner.free(p_rid); + memdelete(reflection_probe); + + return true; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + // delete the texture + LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get_or_null(p_rid); + lightmap_capture->instance_remove_deps(); + + lightmap_capture_data_owner.free(p_rid); + memdelete(lightmap_capture); + return true; + + } else if (canvas_occluder_owner.owns(p_rid)) { + CanvasOccluder *co = canvas_occluder_owner.get_or_null(p_rid); + if (co->index_id) { + glDeleteBuffers(1, &co->index_id); + } + if (co->vertex_id) { + glDeleteBuffers(1, &co->vertex_id); + } + + canvas_occluder_owner.free(p_rid); + memdelete(co); + + return true; + + } else if (canvas_light_shadow_owner.owns(p_rid)) { + CanvasLightShadow *cls = canvas_light_shadow_owner.get_or_null(p_rid); + glDeleteFramebuffers(1, &cls->fbo); + glDeleteRenderbuffers(1, &cls->depth); + glDeleteTextures(1, &cls->distance); + canvas_light_shadow_owner.free(p_rid); + memdelete(cls); + + return true; + */ +} + +bool RasterizerStorageGLES3::has_os_feature(const String &p_feature) const { + if (p_feature == "s3tc") { + return config->s3tc_supported; + } + + if (p_feature == "etc") { + return config->etc_supported; + } + + if (p_feature == "skinning_fallback") { + return config->use_skeleton_software; + } + + return false; +} + +//////////////////////////////////////////// + +void RasterizerStorageGLES3::set_debug_generate_wireframes(bool p_generate) { +} + +//void RasterizerStorageGLES3::render_info_begin_capture() { +// info.snap = info.render; +//} + +//void RasterizerStorageGLES3::render_info_end_capture() { +// info.snap.object_count = info.render.object_count - info.snap.object_count; +// info.snap.draw_call_count = info.render.draw_call_count - info.snap.draw_call_count; +// info.snap.material_switch_count = info.render.material_switch_count - info.snap.material_switch_count; +// info.snap.surface_switch_count = info.render.surface_switch_count - info.snap.surface_switch_count; +// info.snap.shader_rebind_count = info.render.shader_rebind_count - info.snap.shader_rebind_count; +// info.snap.vertices_count = info.render.vertices_count - info.snap.vertices_count; +// info.snap._2d_item_count = info.render._2d_item_count - info.snap._2d_item_count; +// info.snap._2d_draw_call_count = info.render._2d_draw_call_count - info.snap._2d_draw_call_count; +//} + +//int RasterizerStorageGLES3::get_captured_render_info(RS::RenderInfo p_info) { +// switch (p_info) { +// case RS::INFO_OBJECTS_IN_FRAME: { +// return info.snap.object_count; +// } break; +// case RS::INFO_VERTICES_IN_FRAME: { +// return info.snap.vertices_count; +// } break; +// case RS::INFO_MATERIAL_CHANGES_IN_FRAME: { +// return info.snap.material_switch_count; +// } break; +// case RS::INFO_SHADER_CHANGES_IN_FRAME: { +// return info.snap.shader_rebind_count; +// } break; +// case RS::INFO_SURFACE_CHANGES_IN_FRAME: { +// return info.snap.surface_switch_count; +// } break; +// case RS::INFO_DRAW_CALLS_IN_FRAME: { +// return info.snap.draw_call_count; +// } break; +// /* +// case RS::INFO_2D_ITEMS_IN_FRAME: { +// return info.snap._2d_item_count; +// } break; +// case RS::INFO_2D_DRAW_CALLS_IN_FRAME: { +// return info.snap._2d_draw_call_count; +// } break; +// */ +// default: { +// return get_render_info(p_info); +// } +// } +//} + +//int RasterizerStorageGLES3::get_render_info(RS::RenderInfo p_info) { +// switch (p_info) { +// case RS::INFO_OBJECTS_IN_FRAME: +// return info.render_final.object_count; +// case RS::INFO_VERTICES_IN_FRAME: +// return info.render_final.vertices_count; +// case RS::INFO_MATERIAL_CHANGES_IN_FRAME: +// return info.render_final.material_switch_count; +// case RS::INFO_SHADER_CHANGES_IN_FRAME: +// return info.render_final.shader_rebind_count; +// case RS::INFO_SURFACE_CHANGES_IN_FRAME: +// return info.render_final.surface_switch_count; +// case RS::INFO_DRAW_CALLS_IN_FRAME: +// return info.render_final.draw_call_count; +// /* +// case RS::INFO_2D_ITEMS_IN_FRAME: +// return info.render_final._2d_item_count; +// case RS::INFO_2D_DRAW_CALLS_IN_FRAME: +// return info.render_final._2d_draw_call_count; +//*/ +// case RS::INFO_USAGE_VIDEO_MEM_TOTAL: +// return 0; //no idea +// case RS::INFO_VIDEO_MEM_USED: +// return info.vertex_mem + info.texture_mem; +// case RS::INFO_TEXTURE_MEM_USED: +// return info.texture_mem; +// case RS::INFO_VERTEX_MEM_USED: +// return info.vertex_mem; +// default: +// return 0; //no idea either +// } +//} + +String RasterizerStorageGLES3::get_video_adapter_name() const { + return (const char *)glGetString(GL_RENDERER); +} + +String RasterizerStorageGLES3::get_video_adapter_vendor() const { + return (const char *)glGetString(GL_VENDOR); +} + +RenderingDevice::DeviceType RasterizerStorageGLES3::get_video_adapter_type() const { + return RenderingDevice::DeviceType::DEVICE_TYPE_OTHER; +} + +void RasterizerStorageGLES3::initialize() { + RasterizerStorageGLES3::system_fbo = 0; + config = GLES3::Config::get_singleton(); + config->initialize(); + + //determine formats for depth textures (or renderbuffers) + if (config->support_depth_texture) { + // Will use texture for depth + // have to manually see if we can create a valid framebuffer texture using UNSIGNED_INT, + // as there is no extension to test for this. + GLuint fbo; + glGenFramebuffers(1, &fbo); + bind_framebuffer(fbo); + GLuint depth; + glGenTextures(1, &depth); + glBindTexture(GL_TEXTURE_2D, depth); + glTexImage2D(GL_TEXTURE_2D, 0, config->depth_internalformat, 32, 32, 0, GL_DEPTH_COMPONENT, config->depth_type, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + bind_framebuffer_system(); + glDeleteFramebuffers(1, &fbo); + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &depth); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + // If it fails, test to see if it supports a framebuffer texture using UNSIGNED_SHORT + // This is needed because many OSX devices don't support either UNSIGNED_INT or UNSIGNED_SHORT +#ifdef GLES_OVER_GL + config->depth_internalformat = GL_DEPTH_COMPONENT16; +#else + // OES_depth_texture extension only specifies GL_DEPTH_COMPONENT. + config->depth_internalformat = GL_DEPTH_COMPONENT; +#endif + config->depth_type = GL_UNSIGNED_SHORT; + + glGenFramebuffers(1, &fbo); + bind_framebuffer(fbo); + + glGenTextures(1, &depth); + glBindTexture(GL_TEXTURE_2D, depth); + glTexImage2D(GL_TEXTURE_2D, 0, config->depth_internalformat, 32, 32, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth, 0); + + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + //if it fails again depth textures aren't supported, use rgba shadows and renderbuffer for depth + config->support_depth_texture = false; + config->use_rgba_3d_shadows = true; + } + + bind_framebuffer_system(); + glDeleteFramebuffers(1, &fbo); + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &depth); + } + } + + //picky requirements for these + config->support_shadow_cubemaps = config->support_depth_texture && config->support_write_depth && config->support_depth_cubemaps; + + frame.count = 0; + frame.delta = 0; + frame.current_rt = nullptr; + frame.clear_request = false; + + // the use skeleton software path should be used if either float texture is not supported, + // OR max_vertex_texture_image_units is zero + config->use_skeleton_software = (config->float_texture_supported == false) || (config->max_vertex_texture_image_units == 0); + + shaders.copy.initialize(); + shaders.copy_version = shaders.copy.version_create(); //TODO + shaders.copy.version_bind_shader(shaders.copy_version, CopyShaderGLES3::MODE_COPY_SECTION); + //shaders.cubemap_filter.init(); + //bool ggx_hq = GLOBAL_GET("rendering/quality/reflections/high_quality_ggx"); + //shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::LOW_QUALITY, !ggx_hq); + + { + // quad for copying stuff + + glGenBuffers(1, &resources.quadie); + glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); + { + const float qv[16] = { + -1, + -1, + 0, + 0, + -1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + 1, + 0, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + { + //default textures + + glGenTextures(1, &resources.white_tex); + unsigned char whitetexdata[8 * 8 * 3]; + for (int i = 0; i < 8 * 8 * 3; i++) { + whitetexdata[i] = 255; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, resources.white_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, whitetexdata); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenTextures(1, &resources.black_tex); + unsigned char blacktexdata[8 * 8 * 3]; + for (int i = 0; i < 8 * 8 * 3; i++) { + blacktexdata[i] = 0; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, resources.black_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, blacktexdata); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenTextures(1, &resources.normal_tex); + unsigned char normaltexdata[8 * 8 * 3]; + for (int i = 0; i < 8 * 8 * 3; i += 3) { + normaltexdata[i + 0] = 128; + normaltexdata[i + 1] = 128; + normaltexdata[i + 2] = 255; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, resources.normal_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, normaltexdata); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenTextures(1, &resources.aniso_tex); + unsigned char anisotexdata[8 * 8 * 3]; + for (int i = 0; i < 8 * 8 * 3; i += 3) { + anisotexdata[i + 0] = 255; + anisotexdata[i + 1] = 128; + anisotexdata[i + 2] = 0; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, resources.aniso_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, anisotexdata); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + } + + // skeleton buffer + { + resources.skeleton_transform_buffer_size = 0; + glGenBuffers(1, &resources.skeleton_transform_buffer); + } + + // radical inverse vdc cache texture + // used for cubemap filtering + if (true /*||config->float_texture_supported*/) { //uint8 is similar and works everywhere + glGenTextures(1, &resources.radical_inverse_vdc_cache_tex); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); + + uint8_t radical_inverse[512]; + + for (uint32_t i = 0; i < 512; i++) { + uint32_t bits = i; + + bits = (bits << 16) | (bits >> 16); + bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1); + bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2); + bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4); + bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); + + float value = float(bits) * 2.3283064365386963e-10; + radical_inverse[i] = uint8_t(CLAMP(value * 255.0, 0, 255)); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, radical_inverse); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //need this for proper sampling + + glBindTexture(GL_TEXTURE_2D, 0); + } + + { + glGenFramebuffers(1, &resources.mipmap_blur_fbo); + glGenTextures(1, &resources.mipmap_blur_color); + } + +#ifdef GLES_OVER_GL + //this needs to be enabled manually in OpenGL 2.1 + + if (config->extensions.has("GL_ARB_seamless_cube_map")) { + glEnable(_EXT_TEXTURE_CUBE_MAP_SEAMLESS); + } + glEnable(GL_POINT_SPRITE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); +#endif +} + +void RasterizerStorageGLES3::finalize() { +} + +void RasterizerStorageGLES3::_copy_screen() { + bind_quad_array(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +void RasterizerStorageGLES3::update_memory_info() { +} + +uint64_t RasterizerStorageGLES3::get_rendering_info(RS::RenderingInfo p_info) { + return 0; +} + +void RasterizerStorageGLES3::update_dirty_resources() { + update_dirty_shaders(); + update_dirty_materials(); + // update_dirty_skeletons(); + // update_dirty_multimeshes(); +} + +RasterizerStorageGLES3::RasterizerStorageGLES3() { + RasterizerStorageGLES3::system_fbo = 0; +} + +RasterizerStorageGLES3::~RasterizerStorageGLES3() { + shaders.copy.version_free(shaders.copy_version); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h new file mode 100644 index 0000000000..4e04b918c2 --- /dev/null +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -0,0 +1,966 @@ +/*************************************************************************/ +/* rasterizer_storage_gles3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_STORAGE_OPENGL_H +#define RASTERIZER_STORAGE_OPENGL_H + +#ifdef GLES3_ENABLED + +#include "core/templates/local_vector.h" +#include "core/templates/rid_owner.h" +#include "core/templates/self_list.h" +#include "servers/rendering/renderer_compositor.h" +#include "servers/rendering/renderer_storage.h" +#include "servers/rendering/shader_compiler.h" +#include "servers/rendering/shader_language.h" +#include "storage/canvas_texture_storage.h" +#include "storage/config.h" +#include "storage/render_target_storage.h" +#include "storage/texture_storage.h" + +#include "shaders/copy.glsl.gen.h" + +class RasterizerCanvasGLES3; +class RasterizerSceneGLES3; + +class RasterizerStorageGLES3 : public RendererStorage { +public: + RasterizerCanvasGLES3 *canvas; + RasterizerSceneGLES3 *scene; + + static GLuint system_fbo; + + GLES3::Config *config; + + struct Resources { + GLuint white_tex; + GLuint black_tex; + GLuint normal_tex; + GLuint aniso_tex; + + GLuint mipmap_blur_fbo; + GLuint mipmap_blur_color; + + GLuint radical_inverse_vdc_cache_tex; + bool use_rgba_2d_shadows; + + GLuint quadie; + + size_t skeleton_transform_buffer_size; + GLuint skeleton_transform_buffer; + LocalVector<float> skeleton_transform_cpu_buffer; + + } resources; + + mutable struct Shaders { + ShaderCompiler compiler; + + CopyShaderGLES3 copy; + RID copy_version; + //CubemapFilterShaderGLES3 cubemap_filter; + + ShaderCompiler::IdentifierActions actions_canvas; + ShaderCompiler::IdentifierActions actions_scene; + ShaderCompiler::IdentifierActions actions_particles; + + } shaders; + + struct Info { + uint64_t texture_mem = 0; + uint64_t vertex_mem = 0; + + struct Render { + uint32_t object_count; + uint32_t draw_call_count; + uint32_t material_switch_count; + uint32_t surface_switch_count; + uint32_t shader_rebind_count; + uint32_t vertices_count; + uint32_t _2d_item_count; + uint32_t _2d_draw_call_count; + + void reset() { + object_count = 0; + draw_call_count = 0; + material_switch_count = 0; + surface_switch_count = 0; + shader_rebind_count = 0; + vertices_count = 0; + _2d_item_count = 0; + _2d_draw_call_count = 0; + } + } render, render_final, snap; + + Info() { + render.reset(); + render_final.reset(); + } + + } info; + + void bind_quad_array() const; + + ///////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////API//////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////// + +public: + /* SKY API */ + // not sure if used in godot 4? + struct Sky { + RID self; + RID panorama; + GLuint radiance; + int radiance_size; + }; + + mutable RID_PtrOwner<Sky> sky_owner; + + RID sky_create(); + void sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size); + + // SHADER API + + struct Material; + + struct Shader { + RID self; + + RS::ShaderMode mode; + ShaderGLES3 *shader; + String code; + SelfList<Material>::List materials; + + Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + + RID version; + + SelfList<Shader> dirty_list; + + Map<StringName, Map<int, RID>> default_textures; + + Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; + + bool valid; + + String path; + + uint32_t index; + uint64_t last_pass; + + struct CanvasItem { + enum BlendMode { + BLEND_MODE_MIX, + BLEND_MODE_ADD, + BLEND_MODE_SUB, + BLEND_MODE_MUL, + BLEND_MODE_PMALPHA, + }; + + int blend_mode; + + enum LightMode { + LIGHT_MODE_NORMAL, + LIGHT_MODE_UNSHADED, + LIGHT_MODE_LIGHT_ONLY + }; + + int light_mode; + + bool uses_screen_texture; + bool uses_screen_uv; + bool uses_time; + bool uses_modulate; + bool uses_color; + bool uses_vertex; + + // all these should disable item joining if used in a custom shader + bool uses_model_matrix; + bool uses_extra_matrix; + bool uses_projection_matrix; + bool uses_instance_custom; + + } canvas_item; + + struct Spatial { + enum BlendMode { + BLEND_MODE_MIX, + BLEND_MODE_ADD, + BLEND_MODE_SUB, + BLEND_MODE_MUL, + }; + + int blend_mode; + + enum DepthDrawMode { + DEPTH_DRAW_OPAQUE, + DEPTH_DRAW_ALWAYS, + DEPTH_DRAW_NEVER, + DEPTH_DRAW_ALPHA_PREPASS, + }; + + int depth_draw_mode; + + enum CullMode { + CULL_MODE_FRONT, + CULL_MODE_BACK, + CULL_MODE_DISABLED, + }; + + int cull_mode; + + bool uses_alpha; + bool uses_alpha_scissor; + bool unshaded; + bool no_depth_test; + bool uses_vertex; + bool uses_discard; + bool uses_sss; + bool uses_screen_texture; + bool uses_depth_texture; + bool uses_time; + bool uses_tangent; + bool uses_ensure_correct_normals; + bool writes_modelview_or_projection; + bool uses_vertex_lighting; + bool uses_world_coordinates; + + } spatial; + + struct Particles { + } particles; + + bool uses_vertex_time; + bool uses_fragment_time; + + Shader() : + dirty_list(this) { + shader = nullptr; + valid = false; + version = RID(); + last_pass = 0; + } + }; + + mutable RID_PtrOwner<Shader> shader_owner; + mutable SelfList<Shader>::List _shader_dirty_list; + + void _shader_make_dirty(Shader *p_shader); + + RID shader_allocate() override; + void shader_initialize(RID p_rid) override; + + //RID shader_create() override; + + void shader_set_code(RID p_shader, const String &p_code) override; + String shader_get_code(RID p_shader) const override; + void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override; + + void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture, int p_index) override; + RID shader_get_default_texture_param(RID p_shader, const StringName &p_name, int p_index) const override; + + RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override { return RS::ShaderNativeSourceCode(); }; + + void _update_shader(Shader *p_shader) const; + void update_dirty_shaders(); + + // new + Variant shader_get_param_default(RID p_material, const StringName &p_param) const override { return Variant(); } + + // COMMON MATERIAL API + + struct Material { + RID self; + Shader *shader; + Map<StringName, Variant> params; + SelfList<Material> list; + SelfList<Material> dirty_list; + Vector<Pair<StringName, RID>> textures; + float line_width; + int render_priority; + + RID next_pass; + + uint32_t index; + uint64_t last_pass; + + // Map<Geometry *, int> geometry_owners; + // Map<InstanceBaseDependency *, int> instance_owners; + + bool can_cast_shadow_cache; + bool is_animated_cache; + + Material() : + list(this), + dirty_list(this) { + can_cast_shadow_cache = false; + is_animated_cache = false; + shader = nullptr; + line_width = 1.0; + last_pass = 0; + render_priority = 0; + } + }; + + mutable SelfList<Material>::List _material_dirty_list; + void _material_make_dirty(Material *p_material) const; + + // void _material_add_geometry(RID p_material, Geometry *p_geometry); + // void _material_remove_geometry(RID p_material, Geometry *p_geometry); + + void _update_material(Material *p_material); + + mutable RID_PtrOwner<Material> material_owner; + + // new + void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override {} + void material_update_dependency(RID p_material, DependencyTracker *p_instance) override {} + + // old + RID material_allocate() override; + void material_initialize(RID p_rid) override; + + //RID material_create() override; + + void material_set_shader(RID p_material, RID p_shader) override; + RID material_get_shader(RID p_material) const; + + void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) override; + Variant material_get_param(RID p_material, const StringName &p_param) const override; + Variant material_get_param_default(RID p_material, const StringName &p_param) const; + + void material_set_line_width(RID p_material, float p_width); + void material_set_next_pass(RID p_material, RID p_next_material) override; + + bool material_is_animated(RID p_material) override; + bool material_casts_shadows(RID p_material) override; + bool material_uses_tangents(RID p_material); + bool material_uses_ensure_correct_normals(RID p_material); + + void material_add_instance_owner(RID p_material, DependencyTracker *p_instance); + void material_remove_instance_owner(RID p_material, DependencyTracker *p_instance); + + void material_set_render_priority(RID p_material, int priority) override; + + void update_dirty_materials(); + + /* MESH API */ + + RID mesh_allocate() override; + void mesh_initialize(RID p_rid) override; + void mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) override; + bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override; + RID mesh_instance_create(RID p_base) override; + void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override; + void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override; + void mesh_instance_check_for_update(RID p_mesh_instance) override; + void update_mesh_instances() override; + void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override; + float reflection_probe_get_mesh_lod_threshold(RID p_probe) const override; + + void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) override; + + int mesh_get_blend_shape_count(RID p_mesh) const override; + + void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) override; + RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const override; + + void mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; + void mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; + void mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; + + void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override; + RID mesh_surface_get_material(RID p_mesh, int p_surface) const override; + + RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override; + int mesh_get_surface_count(RID p_mesh) const override; + + void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override; + AABB mesh_get_custom_aabb(RID p_mesh) const override; + + AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override; + void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override; + void mesh_clear(RID p_mesh) override; + + /* MULTIMESH API */ + + struct MultiMesh { + RID mesh; + int instances = 0; + RS::MultimeshTransformFormat xform_format = RS::MULTIMESH_TRANSFORM_3D; + bool uses_colors = false; + bool uses_custom_data = false; + int visible_instances = -1; + AABB aabb; + bool aabb_dirty = false; + bool buffer_set = false; + uint32_t stride_cache = 0; + uint32_t color_offset_cache = 0; + uint32_t custom_data_offset_cache = 0; + + Vector<float> data_cache; //used if individual setting is used + bool *data_cache_dirty_regions = nullptr; + uint32_t data_cache_used_dirty_regions = 0; + + RID buffer; //storage buffer + RID uniform_set_3d; + RID uniform_set_2d; + + bool dirty = false; + MultiMesh *dirty_list = nullptr; + + Dependency dependency; + }; + + mutable RID_Owner<MultiMesh, true> multimesh_owner; + + MultiMesh *multimesh_dirty_list = nullptr; + + _FORCE_INLINE_ void _multimesh_make_local(MultiMesh *multimesh) const; + _FORCE_INLINE_ void _multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb); + _FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb); + _FORCE_INLINE_ void _multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances); + void _update_dirty_multimeshes(); + + RID multimesh_allocate() override; + void multimesh_initialize(RID p_rid) override; + 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; + int multimesh_get_instance_count(RID p_multimesh) const override; + + void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; + void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; + void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; + void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; + void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; + + RID multimesh_get_mesh(RID p_multimesh) const override; + AABB multimesh_get_aabb(RID p_multimesh) const override; + + Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; + Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; + Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override; + Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; + void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; + Vector<float> multimesh_get_buffer(RID p_multimesh) const override; + + void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; + int multimesh_get_visible_instances(RID p_multimesh) const override; + + _FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->xform_format; + } + + _FORCE_INLINE_ bool multimesh_uses_colors(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->uses_colors; + } + + _FORCE_INLINE_ bool multimesh_uses_custom_data(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->uses_custom_data; + } + + _FORCE_INLINE_ uint32_t multimesh_get_instances_to_draw(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + if (multimesh->visible_instances >= 0) { + return multimesh->visible_instances; + } + return multimesh->instances; + } + + /* SKELETON API */ + + RID skeleton_allocate() override; + void skeleton_initialize(RID p_rid) override; + void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override; + void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override; + int skeleton_get_bone_count(RID p_skeleton) const override; + void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) override; + Transform3D skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override; + void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) override; + Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const override; + + /* Light API */ + + RID directional_light_allocate() override; + void directional_light_initialize(RID p_rid) override; + RID omni_light_allocate() override; + void omni_light_initialize(RID p_rid) override; + RID spot_light_allocate() override; + void spot_light_initialize(RID p_rid) override; + RID reflection_probe_allocate() override; + void reflection_probe_initialize(RID p_rid) override; + + void light_set_color(RID p_light, const Color &p_color) override; + void light_set_param(RID p_light, RS::LightParam p_param, float p_value) override; + void light_set_shadow(RID p_light, bool p_enabled) override; + void light_set_projector(RID p_light, RID p_texture) override; + void light_set_negative(RID p_light, bool p_enable) override; + void light_set_cull_mask(RID p_light, uint32_t p_mask) override; + void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override; + void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override; + void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override; + void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override; + + void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override; + + void light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) override; + void light_directional_set_blend_splits(RID p_light, bool p_enable) override; + bool light_directional_get_blend_splits(RID p_light) const override; + void light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) override; + RS::LightDirectionalSkyMode light_directional_get_sky_mode(RID p_light) const override; + + RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override; + RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override; + + bool light_has_shadow(RID p_light) const override; + bool light_has_projector(RID p_light) const override; + + RS::LightType light_get_type(RID p_light) const override; + AABB light_get_aabb(RID p_light) const override; + float light_get_param(RID p_light, RS::LightParam p_param) override; + Color light_get_color(RID p_light) override; + RS::LightBakeMode light_get_bake_mode(RID p_light) override; + uint32_t light_get_max_sdfgi_cascade(RID p_light) override; + uint64_t light_get_version(RID p_light) const override; + + /* PROBE API */ + + void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override; + void reflection_probe_set_intensity(RID p_probe, float p_intensity) override; + void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override; + void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override; + void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) override; + void reflection_probe_set_max_distance(RID p_probe, float p_distance) override; + void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) override; + void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) override; + void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override; + void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override; + void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override; + void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override; + void reflection_probe_set_resolution(RID p_probe, int p_resolution) override; + + AABB reflection_probe_get_aabb(RID p_probe) const override; + RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override; + uint32_t reflection_probe_get_cull_mask(RID p_probe) const override; + Vector3 reflection_probe_get_extents(RID p_probe) const override; + Vector3 reflection_probe_get_origin_offset(RID p_probe) const override; + float reflection_probe_get_origin_max_distance(RID p_probe) const override; + bool reflection_probe_renders_shadows(RID p_probe) const override; + + void base_update_dependency(RID p_base, DependencyTracker *p_instance) override; + void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override; + + /* VOXEL GI API */ + + RID voxel_gi_allocate() override; + void voxel_gi_initialize(RID p_rid) override; + void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override; + + AABB voxel_gi_get_bounds(RID p_voxel_gi) const override; + Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override; + Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override; + Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override; + Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override; + + Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override; + Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override; + + void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override; + float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override; + + void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override; + float voxel_gi_get_propagation(RID p_voxel_gi) const override; + + void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override; + float voxel_gi_get_energy(RID p_voxel_gi) const override; + + void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override; + float voxel_gi_get_bias(RID p_voxel_gi) const override; + + void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override; + float voxel_gi_get_normal_bias(RID p_voxel_gi) const override; + + void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override; + bool voxel_gi_is_interior(RID p_voxel_gi) const override; + + void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override; + bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override; + + void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) override; + float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const override; + + uint32_t voxel_gi_get_version(RID p_voxel_gi) override; + + /* LIGHTMAP CAPTURE */ + RID lightmap_allocate() override; + void lightmap_initialize(RID p_rid) override; + void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) override; + void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override; + void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override; + void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override; + PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override; + PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override; + PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override; + PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const override; + AABB lightmap_get_aabb(RID p_lightmap) const override; + void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) override; + bool lightmap_is_interior(RID p_lightmap) const override; + void lightmap_set_probe_capture_update_speed(float p_speed) override; + float lightmap_get_probe_capture_update_speed() const override; + + /* OCCLUDER */ + + void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices); + + /* PARTICLES */ + + RID particles_allocate() override; + void particles_initialize(RID p_rid) override; + void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) override; + void particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) override; + void particles_set_emitting(RID p_particles, bool p_emitting) override; + void particles_set_amount(RID p_particles, int p_amount) override; + void particles_set_lifetime(RID p_particles, double p_lifetime) override; + void particles_set_one_shot(RID p_particles, bool p_one_shot) override; + void particles_set_pre_process_time(RID p_particles, double p_time) override; + void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override; + void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override; + void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override; + void particles_set_speed_scale(RID p_particles, double p_scale) override; + void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override; + void particles_set_process_material(RID p_particles, RID p_material) override; + RID particles_get_process_material(RID p_particles) const override; + void particles_set_fixed_fps(RID p_particles, int p_fps) override; + void particles_set_interpolate(RID p_particles, bool p_enable) override; + void particles_set_fractional_delta(RID p_particles, bool p_enable) override; + void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override; + void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) override; + void particles_set_collision_base_size(RID p_particles, real_t p_size) override; + + void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override; + + void particles_set_trails(RID p_particles, bool p_enable, double p_length) override; + void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) override; + + void particles_restart(RID p_particles) override; + + void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override; + + void particles_set_draw_passes(RID p_particles, int p_count) override; + void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) override; + + void particles_request_process(RID p_particles) override; + AABB particles_get_current_aabb(RID p_particles) override; + AABB particles_get_aabb(RID p_particles) const override; + + void particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) override; + + bool particles_get_emitting(RID p_particles) override; + int particles_get_draw_passes(RID p_particles) const override; + RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const override; + + void particles_add_collision(RID p_particles, RID p_instance) override; + void particles_remove_collision(RID p_particles, RID p_instance) override; + + void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) override; + + void update_particles() override; + + /* PARTICLES COLLISION */ + + RID particles_collision_allocate() override; + void particles_collision_initialize(RID p_rid) override; + void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) override; + void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) override; + void particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) override; + void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) override; + void particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) override; + void particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) override; + void particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) override; + void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) override; + void particles_collision_height_field_update(RID p_particles_collision) override; + void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override; + AABB particles_collision_get_aabb(RID p_particles_collision) const override; + bool particles_collision_is_heightfield(RID p_particles_collision) const override; + RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override; + + RID particles_collision_instance_create(RID p_collision) override; + void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) override; + void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override; + + /* FOG VOLUMES */ + + RID fog_volume_allocate() override; + void fog_volume_initialize(RID p_rid) override; + + void fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) override; + void fog_volume_set_extents(RID p_fog_volume, const Vector3 &p_extents) override; + void fog_volume_set_material(RID p_fog_volume, RID p_material) override; + AABB fog_volume_get_aabb(RID p_fog_volume) const override; + RS::FogVolumeShape fog_volume_get_shape(RID p_fog_volume) const override; + + /* VISIBILITY NOTIFIER */ + RID visibility_notifier_allocate() override; + void visibility_notifier_initialize(RID p_notifier) override; + void visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) override; + void visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) override; + + AABB visibility_notifier_get_aabb(RID p_notifier) const override; + void visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) override; + + /* GLOBAL VARIABLES */ + + void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) override; + void global_variable_remove(const StringName &p_name) override; + Vector<StringName> global_variable_get_list() const override; + + void global_variable_set(const StringName &p_name, const Variant &p_value) override; + void global_variable_set_override(const StringName &p_name, const Variant &p_value) override; + Variant global_variable_get(const StringName &p_name) const override; + RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const override; + + void global_variables_load_settings(bool p_load_textures = true) override; + void global_variables_clear() override; + + int32_t global_variables_instance_allocate(RID p_instance) override; + void global_variables_instance_free(RID p_instance) override; + void global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) override; + + bool particles_is_inactive(RID p_particles) const override; + + // RENDER TARGET + + mutable RID_PtrOwner<GLES3::RenderTarget> render_target_owner; + + void _render_target_clear(GLES3::RenderTarget *rt); + void _render_target_allocate(GLES3::RenderTarget *rt); + void _set_current_render_target(RID p_render_target); + + RID render_target_create() override; + void render_target_set_position(RID p_render_target, int p_x, int p_y) override; + void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override; + Size2i render_target_get_size(RID p_render_target); + RID render_target_get_texture(RID p_render_target) override; + void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override; + + void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) override; + bool render_target_was_used(RID p_render_target) override; + void render_target_clear_used(RID p_render_target); + void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa); + void render_target_set_use_fxaa(RID p_render_target, bool p_fxaa); + void render_target_set_use_debanding(RID p_render_target, bool p_debanding); + + // new + void render_target_set_as_unused(RID p_render_target) override { + render_target_clear_used(p_render_target); + } + + void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override; + bool render_target_is_clear_requested(RID p_render_target) override; + Color render_target_get_clear_request_color(RID p_render_target) override; + void render_target_disable_clear_request(RID p_render_target) override; + void render_target_do_clear_request(RID p_render_target) override; + + void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override; + Rect2i render_target_get_sdf_rect(RID p_render_target) const override; + void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) override; + + // access from canvas + // GLES3::RenderTarget * render_target_get(RID p_render_target); + + /* CANVAS SHADOW */ + + struct CanvasLightShadow { + RID self; + int size; + int height; + GLuint fbo; + GLuint depth; + GLuint distance; //for older devices + }; + + RID_PtrOwner<CanvasLightShadow> canvas_light_shadow_owner; + + RID canvas_light_shadow_buffer_create(int p_width); + + /* LIGHT SHADOW MAPPING */ + /* + struct CanvasOccluder { + RID self; + + GLuint vertex_id; // 0 means, unconfigured + GLuint index_id; // 0 means, unconfigured + LocalVector<Vector2> lines; + int len; + }; + + RID_Owner<CanvasOccluder> canvas_occluder_owner; + + RID canvas_light_occluder_create(); + void canvas_light_occluder_set_polylines(RID p_occluder, const LocalVector<Vector2> &p_lines); +*/ + + RS::InstanceType get_base_type(RID p_rid) const override; + + bool free(RID p_rid) override; + + struct Frame { + GLES3::RenderTarget *current_rt; + + // these 2 may have been superseded by the equivalents in the render target. + // these may be able to be removed. + bool clear_request; + Color clear_request_color; + + float time; + float delta; + uint64_t count; + + Frame() { + // current_rt = nullptr; + // clear_request = false; + } + } frame; + + void initialize(); + void finalize(); + + void _copy_screen(); + + void update_memory_info() override; + uint64_t get_rendering_info(RS::RenderingInfo p_info) override; + + bool has_os_feature(const String &p_feature) const override; + + void update_dirty_resources() override; + + void set_debug_generate_wireframes(bool p_generate) override; + + // void render_info_begin_capture() override; + // void render_info_end_capture() override; + // int get_captured_render_info(RS::RenderInfo p_info) override; + + // int get_render_info(RS::RenderInfo p_info) override; + String get_video_adapter_name() const override; + String get_video_adapter_vendor() const override; + RenderingDevice::DeviceType get_video_adapter_type() const override; + + void capture_timestamps_begin() override {} + void capture_timestamp(const String &p_name) override {} + uint32_t get_captured_timestamps_count() const override { + return 0; + } + uint64_t get_captured_timestamps_frame() const override { + return 0; + } + uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override { + return 0; + } + uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override { + return 0; + } + String get_captured_timestamp_name(uint32_t p_index) const override { + return String(); + } + + // make access easier to these + struct Dimensions { + // render target + int rt_width; + int rt_height; + + // window + int win_width; + int win_height; + Dimensions() { + rt_width = 0; + rt_height = 0; + win_width = 0; + win_height = 0; + } + } _dims; + + void buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target = GL_ARRAY_BUFFER, GLenum p_usage = GL_DYNAMIC_DRAW, bool p_optional_orphan = false) const; + bool safe_buffer_sub_data(unsigned int p_total_buffer_size, GLenum p_target, unsigned int p_offset, unsigned int p_data_size, const void *p_data, unsigned int &r_offset_after) const; + + void bind_framebuffer(GLuint framebuffer) { + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + } + + void bind_framebuffer_system() { + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); + } + + RasterizerStorageGLES3(); + ~RasterizerStorageGLES3(); +}; + +inline bool RasterizerStorageGLES3::safe_buffer_sub_data(unsigned int p_total_buffer_size, GLenum p_target, unsigned int p_offset, unsigned int p_data_size, const void *p_data, unsigned int &r_offset_after) const { + r_offset_after = p_offset + p_data_size; +#ifdef DEBUG_ENABLED + // we are trying to write across the edge of the buffer + if (r_offset_after > p_total_buffer_size) { + return false; + } +#endif + glBufferSubData(p_target, p_offset, p_data_size, p_data); + return true; +} + +// standardize the orphan / upload in one place so it can be changed per platform as necessary, and avoid future +// bugs causing pipeline stalls +inline void RasterizerStorageGLES3::buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target, GLenum p_usage, bool p_optional_orphan) const { + // Orphan the buffer to avoid CPU/GPU sync points caused by glBufferSubData + // Was previously #ifndef GLES_OVER_GL however this causes stalls on desktop mac also (and possibly other) + if (!p_optional_orphan || (config->should_orphan)) { + glBufferData(p_target, p_buffer_size, nullptr, p_usage); +#ifdef RASTERIZER_EXTRA_CHECKS + // fill with garbage off the end of the array + if (p_buffer_size) { + unsigned int start = p_offset + p_data_size; + unsigned int end = start + 1024; + if (end < p_buffer_size) { + uint8_t *garbage = (uint8_t *)alloca(1024); + for (int n = 0; n < 1024; n++) { + garbage[n] = Math::random(0, 255); + } + glBufferSubData(p_target, start, 1024, garbage); + } + } +#endif + } + glBufferSubData(p_target, p_offset, p_data_size, p_data); +} + +#endif // GLES3_ENABLED + +#endif // RASTERIZER_STORAGE_OPENGL_H diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp new file mode 100644 index 0000000000..9349722625 --- /dev/null +++ b/drivers/gles3/shader_gles3.cpp @@ -0,0 +1,703 @@ +/*************************************************************************/ +/* shader_gles3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shader_gles3.h" + +#ifdef GLES3_ENABLED + +#include "core/io/compression.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" + +void ShaderGLES3::_add_stage(const char *p_code, StageType p_stage_type) { + Vector<String> lines = String(p_code).split("\n"); + + String text; + + for (int i = 0; i < lines.size(); i++) { + String l = lines[i]; + bool push_chunk = false; + + StageTemplate::Chunk chunk; + + if (l.begins_with("#GLOBALS")) { + switch (p_stage_type) { + case STAGE_TYPE_VERTEX: + chunk.type = StageTemplate::Chunk::TYPE_VERTEX_GLOBALS; + break; + case STAGE_TYPE_FRAGMENT: + chunk.type = StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS; + break; + default: { + } + } + + push_chunk = true; + } else if (l.begins_with("#MATERIAL_UNIFORMS")) { + chunk.type = StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS; + push_chunk = true; + } else if (l.begins_with("#CODE")) { + chunk.type = StageTemplate::Chunk::TYPE_CODE; + push_chunk = true; + chunk.code = l.replace_first("#CODE", String()).replace(":", "").strip_edges().to_upper(); + } else { + text += l + "\n"; + } + + if (push_chunk) { + if (text != String()) { + StageTemplate::Chunk text_chunk; + text_chunk.type = StageTemplate::Chunk::TYPE_TEXT; + text_chunk.text = text.utf8(); + stage_templates[p_stage_type].chunks.push_back(text_chunk); + text = String(); + } + stage_templates[p_stage_type].chunks.push_back(chunk); + } + + if (text != String()) { + StageTemplate::Chunk text_chunk; + text_chunk.type = StageTemplate::Chunk::TYPE_TEXT; + text_chunk.text = text.utf8(); + stage_templates[p_stage_type].chunks.push_back(text_chunk); + text = String(); + } + } +} + +void ShaderGLES3::_setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants) { + name = p_name; + + if (p_vertex_code) { + _add_stage(p_vertex_code, STAGE_TYPE_VERTEX); + } + if (p_fragment_code) { + _add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT); + } + + uniform_names = p_uniform_names; + uniform_count = p_uniform_count; + ubo_pairs = p_ubos; + ubo_count = p_ubo_count; + texunit_pairs = p_tex_units; + texunit_pair_count = p_texture_count; + specializations = p_specializations; + specialization_count = p_specialization_count; + specialization_default_mask = 0; + for (int i = 0; i < specialization_count; i++) { + if (specializations[i].default_value) { + specialization_default_mask |= (uint64_t(1) << uint64_t(i)); + } + } + variant_defines = p_variants; + variant_count = p_variant_count; + + StringBuilder tohash; + /* + tohash.append("[SpirvCacheKey]"); + tohash.append(RenderingDevice::get_singleton()->shader_get_spirv_cache_key()); + tohash.append("[BinaryCacheKey]"); + tohash.append(RenderingDevice::get_singleton()->shader_get_binary_cache_key()); + */ + tohash.append("[Vertex]"); + tohash.append(p_vertex_code ? p_vertex_code : ""); + tohash.append("[Fragment]"); + tohash.append(p_fragment_code ? p_fragment_code : ""); + + base_sha256 = tohash.as_string().sha256_text(); +} + +RID ShaderGLES3::version_create() { + //initialize() was never called + ERR_FAIL_COND_V(variant_count == 0, RID()); + + Version version; + return version_owner.make_rid(version); +} + +void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization) { +#ifdef GLES_OVER_GL + builder.append("#version 330\n"); + builder.append("#define USE_GLES_OVER_GL\n"); +#else + builder.append("#version 300 es\n"); +#endif + + for (int i = 0; i < specialization_count; i++) { + if (p_specialization & (uint64_t(1) << uint64_t(i))) { + builder.append("#define " + String(specializations[i].name) + "\n"); + } + } + if (p_version->uniforms.size()) { + builder.append("#define MATERIAL_UNIFORMS_USED\n"); + } + for (const KeyValue<StringName, CharString> &E : p_version->code_sections) { + builder.append(String("#define ") + String(E.key) + "_CODE_USED\n"); + } + + builder.append("\n"); //make sure defines begin at newline + builder.append(general_defines.get_data()); + builder.append(variant_defines[p_variant]); + for (int j = 0; j < p_version->custom_defines.size(); j++) { + builder.append(p_version->custom_defines[j].get_data()); + } + builder.append("\n"); //make sure defines begin at newline + + for (uint32_t i = 0; i < p_template.chunks.size(); i++) { + const StageTemplate::Chunk &chunk = p_template.chunks[i]; + switch (chunk.type) { + case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: { + builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) + } break; + case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: { + builder.append(p_version->vertex_globals.get_data()); // vertex globals + } break; + case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: { + builder.append(p_version->fragment_globals.get_data()); // fragment globals + } break; + case StageTemplate::Chunk::TYPE_CODE: { + if (p_version->code_sections.has(chunk.code)) { + builder.append(p_version->code_sections[chunk.code].get_data()); + } + } break; + case StageTemplate::Chunk::TYPE_TEXT: { + builder.append(chunk.text.get_data()); + } break; + } + } +} + +static void _display_error_with_code(const String &p_error, const String &p_code) { + int line = 1; + Vector<String> lines = p_code.split("\n"); + + for (int j = 0; j < lines.size(); j++) { + print_line(itos(line) + ": " + lines[j]); + line++; + } + + ERR_PRINT(p_error); +} + +void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization) { + spec.id = glCreateProgram(); + spec.ok = false; + GLint status; + + //vertex stage + { + StringBuilder builder; + _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX], p_specialization); + + spec.vert_id = glCreateShader(GL_VERTEX_SHADER); + String builder_string = builder.as_string(); + CharString cs = builder_string.utf8(); + const char *cstr = cs.ptr(); + glShaderSource(spec.vert_id, 1, &cstr, nullptr); + glCompileShader(spec.vert_id); + + glGetShaderiv(spec.vert_id, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) { + GLsizei iloglen; + glGetShaderiv(spec.vert_id, GL_INFO_LOG_LENGTH, &iloglen); + + if (iloglen < 0) { + glDeleteShader(spec.vert_id); + glDeleteProgram(spec.id); + spec.id = 0; + + ERR_PRINT("No OpenGL vertex shader compiler log."); + } else { + if (iloglen == 0) { + iloglen = 4096; // buggy driver (Adreno 220+) + } + + char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); + ilogmem[iloglen] = '\0'; + glGetShaderInfoLog(spec.vert_id, iloglen, &iloglen, ilogmem); + + String err_string = name + ": Vertex shader compilation failed:\n"; + + err_string += ilogmem; + + _display_error_with_code(err_string, builder_string); + + Memory::free_static(ilogmem); + glDeleteShader(spec.vert_id); + glDeleteProgram(spec.id); + spec.id = 0; + } + + ERR_FAIL(); + } + } + + //fragment stage + { + StringBuilder builder; + _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT], p_specialization); + + spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER); + String builder_string = builder.as_string(); + CharString cs = builder_string.utf8(); + const char *cstr = cs.ptr(); + glShaderSource(spec.frag_id, 1, &cstr, nullptr); + glCompileShader(spec.frag_id); + + glGetShaderiv(spec.frag_id, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) { + GLsizei iloglen; + glGetShaderiv(spec.frag_id, GL_INFO_LOG_LENGTH, &iloglen); + + if (iloglen < 0) { + glDeleteShader(spec.frag_id); + glDeleteProgram(spec.id); + spec.id = 0; + + ERR_PRINT("No OpenGL fragment shader compiler log."); + } else { + if (iloglen == 0) { + iloglen = 4096; // buggy driver (Adreno 220+) + } + + char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); + ilogmem[iloglen] = '\0'; + glGetShaderInfoLog(spec.frag_id, iloglen, &iloglen, ilogmem); + + String err_string = name + ": Fragment shader compilation failed:\n"; + + err_string += ilogmem; + + _display_error_with_code(err_string, builder_string); + + Memory::free_static(ilogmem); + glDeleteShader(spec.frag_id); + glDeleteProgram(spec.id); + spec.id = 0; + } + + ERR_FAIL(); + } + } + + glAttachShader(spec.id, spec.frag_id); + glAttachShader(spec.id, spec.vert_id); + + //for (int i = 0; i < attribute_pair_count; i++) { + // glBindAttribLocation(v.id, attribute_pairs[i].index, attribute_pairs[i].name); + //} + + glLinkProgram(spec.id); + + glGetProgramiv(spec.id, GL_LINK_STATUS, &status); + if (status == GL_FALSE) { + GLsizei iloglen; + glGetProgramiv(spec.id, GL_INFO_LOG_LENGTH, &iloglen); + + if (iloglen < 0) { + glDeleteShader(spec.frag_id); + glDeleteShader(spec.vert_id); + glDeleteProgram(spec.id); + spec.id = 0; + + ERR_PRINT("No OpenGL program link log. What the frick?"); + ERR_FAIL(); + } + + if (iloglen == 0) { + iloglen = 4096; // buggy driver (Adreno 220+) + } + + char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); + ilogmem[iloglen] = '\0'; + glGetProgramInfoLog(spec.id, iloglen, &iloglen, ilogmem); + + String err_string = name + ": Program linking failed:\n"; + + err_string += ilogmem; + + _display_error_with_code(err_string, String()); + + Memory::free_static(ilogmem); + glDeleteShader(spec.frag_id); + glDeleteShader(spec.vert_id); + glDeleteProgram(spec.id); + spec.id = 0; + + ERR_FAIL(); + } + + // get uniform locations + + glUseProgram(spec.id); + + spec.uniform_location.resize(uniform_count); + for (int i = 0; i < uniform_count; i++) { + spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]); + } + + for (int i = 0; i < texunit_pair_count; i++) { + GLint loc = glGetUniformLocation(spec.id, texunit_pairs[i].name); + if (loc >= 0) { + if (texunit_pairs[i].index < 0) { + glUniform1i(loc, max_image_units + texunit_pairs[i].index); + } else { + glUniform1i(loc, texunit_pairs[i].index); + } + } + } + + for (int i = 0; i < ubo_count; i++) { + GLint loc = glGetUniformBlockIndex(spec.id, ubo_pairs[i].name); + if (loc >= 0) { + glUniformBlockBinding(spec.id, loc, ubo_pairs[i].index); + } + } + // textures + for (int i = 0; i < p_version->texture_uniforms.size(); i++) { + String native_uniform_name = p_version->texture_uniforms[i]; + GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data()); + glUniform1i(location, i + base_texture_index); + } + + glUseProgram(0); + spec.ok = true; +} + +RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_version) { + Version *version = version_owner.get_or_null(p_version); + RS::ShaderNativeSourceCode source_code; + ERR_FAIL_COND_V(!version, source_code); + + source_code.versions.resize(variant_count); + + for (int i = 0; i < source_code.versions.size(); i++) { + //vertex stage + + { + StringBuilder builder; + _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX], specialization_default_mask); + + RS::ShaderNativeSourceCode::Version::Stage stage; + stage.name = "vertex"; + stage.code = builder.as_string(); + + source_code.versions.write[i].stages.push_back(stage); + } + + //fragment stage + { + StringBuilder builder; + _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT], specialization_default_mask); + + RS::ShaderNativeSourceCode::Version::Stage stage; + stage.name = "fragment"; + stage.code = builder.as_string(); + + source_code.versions.write[i].stages.push_back(stage); + } + } + + return source_code; +} + +String ShaderGLES3::_version_get_sha1(Version *p_version) const { + StringBuilder hash_build; + + hash_build.append("[uniforms]"); + hash_build.append(p_version->uniforms.get_data()); + hash_build.append("[vertex_globals]"); + hash_build.append(p_version->vertex_globals.get_data()); + hash_build.append("[fragment_globals]"); + hash_build.append(p_version->fragment_globals.get_data()); + + Vector<StringName> code_sections; + for (const KeyValue<StringName, CharString> &E : p_version->code_sections) { + code_sections.push_back(E.key); + } + code_sections.sort_custom<StringName::AlphCompare>(); + + for (int i = 0; i < code_sections.size(); i++) { + hash_build.append(String("[code:") + String(code_sections[i]) + "]"); + hash_build.append(p_version->code_sections[code_sections[i]].get_data()); + } + for (int i = 0; i < p_version->custom_defines.size(); i++) { + hash_build.append("[custom_defines:" + itos(i) + "]"); + hash_build.append(p_version->custom_defines[i].get_data()); + } + + return hash_build.as_string().sha1_text(); +} + +//static const char *shader_file_header = "GLSC"; +//static const uint32_t cache_file_version = 2; + +bool ShaderGLES3::_load_from_cache(Version *p_version) { +#if 0 + String sha1 = _version_get_sha1(p_version); + String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; + + FileAccessRef f = FileAccess::open(path, FileAccess::READ); + if (!f) { + return false; + } + + char header[5] = { 0, 0, 0, 0, 0 }; + f->get_buffer((uint8_t *)header, 4); + ERR_FAIL_COND_V(header != String(shader_file_header), false); + + uint32_t file_version = f->get_32(); + if (file_version != cache_file_version) { + return false; // wrong version + } + + uint32_t variant_count = f->get_32(); + + ERR_FAIL_COND_V(variant_count != (uint32_t)variant_count, false); //should not happen but check + + for (uint32_t i = 0; i < variant_count; i++) { + uint32_t variant_size = f->get_32(); + ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[i], false); + if (!variants_enabled[i]) { + continue; + } + Vector<uint8_t> variant_bytes; + variant_bytes.resize(variant_size); + + uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size); + + ERR_FAIL_COND_V(br != variant_size, false); + + p_version->variant_data[i] = variant_bytes; + } + + for (uint32_t i = 0; i < variant_count; i++) { + if (!variants_enabled[i]) { + MutexLock lock(variant_set_mutex); + p_version->variants[i] = RID(); + continue; + } + RID shader = GLES3::get_singleton()->shader_create_from_bytecode(p_version->variant_data[i]); + if (shader.is_null()) { + for (uint32_t j = 0; j < i; j++) { + GLES3::get_singleton()->free(p_version->variants[i]); + } + ERR_FAIL_COND_V(shader.is_null(), false); + } + { + MutexLock lock(variant_set_mutex); + p_version->variants[i] = shader; + } + } + + memdelete_arr(p_version->variant_data); //clear stages + p_version->variant_data = nullptr; + p_version->valid = true; + return true; +#endif + return false; +} + +void ShaderGLES3::_save_to_cache(Version *p_version) { +#if 0 + String sha1 = _version_get_sha1(p_version); + String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; + + FileAccessRef f = FileAccess::open(path, FileAccess::WRITE); + ERR_FAIL_COND(!f); + f->store_buffer((const uint8_t *)shader_file_header, 4); + f->store_32(cache_file_version); //file version + uint32_t variant_count = variant_count; + f->store_32(variant_count); //variant count + + for (uint32_t i = 0; i < variant_count; i++) { + f->store_32(p_version->variant_data[i].size()); //stage count + f->store_buffer(p_version->variant_data[i].ptr(), p_version->variant_data[i].size()); + } + + f->close(); +#endif +} + +void ShaderGLES3::_clear_version(Version *p_version) { + // Variants not compiled yet, just return + if (p_version->variants.size() == 0) { + return; + } + + for (int i = 0; i < variant_count; i++) { + for (OAHashMap<uint64_t, Version::Specialization>::Iterator it = p_version->variants[i].iter(); it.valid; it = p_version->variants[i].next_iter(it)) { + if (it.valid) { + glDeleteShader(it.value->vert_id); + glDeleteShader(it.value->frag_id); + glDeleteProgram(it.value->id); + } + } + } + + p_version->variants.clear(); +} + +void ShaderGLES3::_initialize_version(Version *p_version) { + ERR_FAIL_COND(p_version->variants.size() > 0); + p_version->variants.reserve(variant_count); + for (int i = 0; i < variant_count; i++) { + OAHashMap<uint64_t, Version::Specialization> variant; + p_version->variants.push_back(variant); + Version::Specialization spec; + _compile_specialization(spec, i, p_version, specialization_default_mask); + p_version->variants[i].insert(specialization_default_mask, spec); + } +} + +void ShaderGLES3::version_set_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const Vector<StringName> &p_texture_uniforms, bool p_initialize) { + Version *version = version_owner.get_or_null(p_version); + ERR_FAIL_COND(!version); + + _clear_version(version); //clear if existing + + version->vertex_globals = p_vertex_globals.utf8(); + version->fragment_globals = p_fragment_globals.utf8(); + version->uniforms = p_uniforms.utf8(); + version->code_sections.clear(); + version->texture_uniforms = p_texture_uniforms; + for (const KeyValue<String, String> &E : p_code) { + version->code_sections[StringName(E.key.to_upper())] = E.value.utf8(); + } + + version->custom_defines.clear(); + for (int i = 0; i < p_custom_defines.size(); i++) { + version->custom_defines.push_back(p_custom_defines[i].utf8()); + } + + if (p_initialize) { + _initialize_version(version); + } +} + +bool ShaderGLES3::version_is_valid(RID p_version) { + Version *version = version_owner.get_or_null(p_version); + return version != nullptr; +} + +bool ShaderGLES3::version_free(RID p_version) { + if (version_owner.owns(p_version)) { + Version *version = version_owner.get_or_null(p_version); + _clear_version(version); + version_owner.free(p_version); + } else { + return false; + } + + return true; +} + +bool ShaderGLES3::shader_cache_cleanup_on_start = false; + +ShaderGLES3::ShaderGLES3() { +} + +void ShaderGLES3::initialize(const String &p_general_defines, int p_base_texture_index) { + general_defines = p_general_defines.utf8(); + base_texture_index = p_base_texture_index; + + _init(); + + if (shader_cache_dir != String()) { + StringBuilder hash_build; + + hash_build.append("[base_hash]"); + hash_build.append(base_sha256); + hash_build.append("[general_defines]"); + hash_build.append(general_defines.get_data()); + for (int i = 0; i < variant_count; i++) { + hash_build.append("[variant_defines:" + itos(i) + "]"); + hash_build.append(variant_defines[i]); + } + + base_sha256 = hash_build.as_string().sha256_text(); + + DirAccessRef d = DirAccess::open(shader_cache_dir); + ERR_FAIL_COND(!d); + if (d->change_dir(name) != OK) { + Error err = d->make_dir(name); + ERR_FAIL_COND(err != OK); + d->change_dir(name); + } + + //erase other versions? + if (shader_cache_cleanup_on_start) { + } + // + if (d->change_dir(base_sha256) != OK) { + Error err = d->make_dir(base_sha256); + ERR_FAIL_COND(err != OK); + } + shader_cache_dir_valid = true; + + print_verbose("Shader '" + name + "' SHA256: " + base_sha256); + } + + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_image_units); +} + +void ShaderGLES3::set_shader_cache_dir(const String &p_dir) { + shader_cache_dir = p_dir; +} + +void ShaderGLES3::set_shader_cache_save_compressed(bool p_enable) { + shader_cache_save_compressed = p_enable; +} + +void ShaderGLES3::set_shader_cache_save_compressed_zstd(bool p_enable) { + shader_cache_save_compressed_zstd = p_enable; +} + +void ShaderGLES3::set_shader_cache_save_debug(bool p_enable) { + shader_cache_save_debug = p_enable; +} + +String ShaderGLES3::shader_cache_dir; +bool ShaderGLES3::shader_cache_save_compressed = true; +bool ShaderGLES3::shader_cache_save_compressed_zstd = true; +bool ShaderGLES3::shader_cache_save_debug = true; + +ShaderGLES3::~ShaderGLES3() { + List<RID> remaining; + version_owner.get_owned_list(&remaining); + if (remaining.size()) { + ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed"); + while (remaining.size()) { + version_free(remaining.front()->get()); + remaining.pop_front(); + } + } +} +#endif diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h new file mode 100644 index 0000000000..f344ea047f --- /dev/null +++ b/drivers/gles3/shader_gles3.h @@ -0,0 +1,246 @@ +/*************************************************************************/ +/* shader_gles3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHADER_OPENGL_H +#define SHADER_OPENGL_H + +#include "core/os/mutex.h" +#include "core/string/string_builder.h" +#include "core/templates/hash_map.h" +#include "core/templates/local_vector.h" +#include "core/templates/map.h" +#include "core/templates/rid_owner.h" +#include "core/variant/variant.h" +#include "servers/rendering_server.h" + +#ifdef GLES3_ENABLED + +// This must come first to avoid windows.h mess +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif + +#include <stdio.h> + +class ShaderGLES3 { +protected: + struct TexUnitPair { + const char *name; + int index; + }; + + struct UBOPair { + const char *name; + int index; + }; + + struct Specialization { + const char *name; + bool default_value = false; + }; + +private: + //versions + CharString general_defines; + + // A version is a high-level construct which is a combination of built-in and user-defined shader code + // Variants use #idefs to toggle behaviour on and off to change behaviour of the shader + // Specializations use #ifdefs to toggle behaviour on and off for performance, on supporting hardware, they will compile a version with everything enabled, and then compile more copies to improve performance + // Use specializations to enable and disabled advanced features, use variants to toggle behaviour when different data may be used (e.g. using a samplerArray vs a sampler) + struct Version { + Vector<StringName> texture_uniforms; + CharString uniforms; + CharString vertex_globals; + CharString fragment_globals; + Map<StringName, CharString> code_sections; + Vector<CharString> custom_defines; + + struct Specialization { + GLuint id; + GLuint vert_id; + GLuint frag_id; + LocalVector<GLint> uniform_location; + LocalVector<GLint> texture_uniform_locations; + Map<StringName, GLint> custom_uniform_locations; + bool build_queued = false; + bool ok = false; + Specialization() { + id = 0; + vert_id = 0; + frag_id = 0; + } + }; + + LocalVector<OAHashMap<uint64_t, Specialization>> variants; + }; + + Mutex variant_set_mutex; + + void _compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization); + + void _clear_version(Version *p_version); + void _initialize_version(Version *p_version); + + RID_Owner<Version> version_owner; + + struct StageTemplate { + struct Chunk { + enum Type { + TYPE_MATERIAL_UNIFORMS, + TYPE_VERTEX_GLOBALS, + TYPE_FRAGMENT_GLOBALS, + TYPE_CODE, + TYPE_TEXT + }; + + Type type; + StringName code; + CharString text; + }; + LocalVector<Chunk> chunks; + }; + + String name; + + String base_sha256; + + static String shader_cache_dir; + static bool shader_cache_cleanup_on_start; + static bool shader_cache_save_compressed; + static bool shader_cache_save_compressed_zstd; + static bool shader_cache_save_debug; + bool shader_cache_dir_valid = false; + + GLint max_image_units; + + enum StageType { + STAGE_TYPE_VERTEX, + STAGE_TYPE_FRAGMENT, + STAGE_TYPE_MAX, + }; + + StageTemplate stage_templates[STAGE_TYPE_MAX]; + + void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization); + + void _add_stage(const char *p_code, StageType p_stage_type); + + String _version_get_sha1(Version *p_version) const; + bool _load_from_cache(Version *p_version); + void _save_to_cache(Version *p_version); + + const char **uniform_names = nullptr; + int uniform_count = 0; + const UBOPair *ubo_pairs = nullptr; + int ubo_count = 0; + const TexUnitPair *texunit_pairs = nullptr; + int texunit_pair_count = 0; + int specialization_count = 0; + const Specialization *specializations = nullptr; + uint64_t specialization_default_mask = 0; + const char **variant_defines = nullptr; + int variant_count = 0; + + int base_texture_index = 0; + Version::Specialization *current_shader = nullptr; + +protected: + ShaderGLES3(); + void _setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants); + + _FORCE_INLINE_ void _version_bind_shader(RID p_version, int p_variant, uint64_t p_specialization) { + ERR_FAIL_INDEX(p_variant, variant_count); + + Version *version = version_owner.get_or_null(p_version); + ERR_FAIL_COND(!version); + + if (version->variants.size() == 0) { + _initialize_version(version); //may lack initialization + } + + Version::Specialization *spec = version->variants[p_variant].lookup_ptr(p_specialization); + if (!spec) { + if (false) { + // Queue load this specialization and use defaults in the meantime (TODO) + + spec = version->variants[p_variant].lookup_ptr(specialization_default_mask); + } else { + // Compile on the spot + Version::Specialization s; + _compile_specialization(s, p_variant, version, p_specialization); + version->variants[p_variant].insert(p_specialization, s); + spec = version->variants[p_variant].lookup_ptr(p_specialization); + } + } else if (spec->build_queued) { + // Still queued, wait + spec = version->variants[p_variant].lookup_ptr(specialization_default_mask); + } + + ERR_FAIL_COND(!spec); // Should never happen + ERR_FAIL_COND(!spec->ok); // Should never happen + + glUseProgram(spec->id); + current_shader = spec; + } + + _FORCE_INLINE_ int _version_get_uniform(int p_which, RID p_version, int p_variant, uint64_t p_specialization) { + ERR_FAIL_INDEX_V(p_which, uniform_count, -1); + Version *version = version_owner.get_or_null(p_version); + ERR_FAIL_COND_V(!version, -1); + return version->variants[p_variant].lookup_ptr(p_specialization)->uniform_location[p_which]; + } + + virtual void _init() = 0; + +public: + RID version_create(); + + void version_set_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const Vector<StringName> &p_texture_uniforms, bool p_initialize = false); + + bool version_is_valid(RID p_version); + + bool version_free(RID p_version); + + static void set_shader_cache_dir(const String &p_dir); + static void set_shader_cache_save_compressed(bool p_enable); + static void set_shader_cache_save_compressed_zstd(bool p_enable); + static void set_shader_cache_save_debug(bool p_enable); + + RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version); + + void initialize(const String &p_general_defines = "", int p_base_texture_index = 0); + virtual ~ShaderGLES3(); +}; + +#endif // SHADER_OPENGL_H +#endif diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub new file mode 100644 index 0000000000..2f56b77bdc --- /dev/null +++ b/drivers/gles3/shaders/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import("env") + +if "GLES3_GLSL" in env["BUILDERS"]: + env.GLES3_GLSL("canvas.glsl") + env.GLES3_GLSL("copy.glsl") diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl new file mode 100644 index 0000000000..8812447f6e --- /dev/null +++ b/drivers/gles3/shaders/canvas.glsl @@ -0,0 +1,753 @@ +/* clang-format off */ +#[modes] + +mode_quad = +mode_ninepatch = #define USE_NINEPATCH +mode_primitive = #define USE_PRIMITIVE +mode_attributes = #define USE_ATTRIBUTES + +#[specializations] + +DISABLE_LIGHTING = false + +#[vertex] + +#ifdef USE_ATTRIBUTES +layout(location = 0) in vec2 vertex_attrib; +layout(location = 3) in vec4 color_attrib; +layout(location = 4) in vec2 uv_attrib; + +layout(location = 10) in uvec4 bone_attrib; +layout(location = 11) in vec4 weight_attrib; + +#endif +/* clang-format on */ +#include "canvas_uniforms_inc.glsl" +#include "stdlib_inc.glsl" + +uniform sampler2D transforms_texture; //texunit:-1 + +out vec2 uv_interp; +out vec4 color_interp; +out vec2 vertex_interp; +flat out int draw_data_instance; + +#ifdef USE_NINEPATCH + +out vec2 pixel_size_interp; + +#endif + +#ifdef MATERIAL_UNIFORMS_USED +layout(std140) uniform MaterialUniforms{ +//ubo:4 + +#MATERIAL_UNIFORMS + +}; +#endif + +#GLOBALS + +void main() { + vec4 instance_custom = vec4(0.0); + draw_data_instance = gl_InstanceID; +#ifdef USE_PRIMITIVE + + //weird bug, + //this works + vec2 vertex; + vec2 uv; + vec4 color; + + if (gl_VertexID == 0) { + vertex = draw_data[draw_data_instance].point_a; + uv = draw_data[draw_data_instance].uv_a; + color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_a_rg), unpackHalf2x16(draw_data[draw_data_instance].color_a_ba)); + } else if (gl_VertexID == 1) { + vertex = draw_data[draw_data_instance].point_b; + uv = draw_data[draw_data_instance].uv_b; + color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_b_rg), unpackHalf2x16(draw_data[draw_data_instance].color_b_ba)); + } else { + vertex = draw_data[draw_data_instance].point_c; + uv = draw_data[draw_data_instance].uv_c; + color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_c_rg), unpackHalf2x16(draw_data[draw_data_instance].color_c_ba)); + } + uvec4 bones = uvec4(0, 0, 0, 0); + vec4 bone_weights = vec4(0.0); + +#elif defined(USE_ATTRIBUTES) + + vec2 vertex = vertex_attrib; + vec4 color = color_attrib * draw_data[draw_data_instance].modulation; + vec2 uv = uv_attrib; + + uvec4 bones = bone_attrib; + vec4 bone_weights = weight_attrib; +#else + + vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); + vec2 vertex_base = vertex_base_arr[gl_VertexID]; + + vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy); + vec4 color = draw_data[draw_data_instance].modulation; + vec2 vertex = draw_data[draw_data_instance].dst_rect.xy + abs(draw_data[draw_data_instance].dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data[draw_data_instance].src_rect.zw, vec2(0.0, 0.0))); + uvec4 bones = uvec4(0, 0, 0, 0); + +#endif + + mat4 model_matrix = mat4(vec4(draw_data[draw_data_instance].world_x, 0.0, 0.0), vec4(draw_data[draw_data_instance].world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data[draw_data_instance].world_ofs, 0.0, 1.0)); + + // MultiMeshes don't batch, so always read from draw_data[0] + uint instancing = draw_data[0].flags & FLAGS_INSTANCING_MASK; + +#ifdef USE_ATTRIBUTES +/* + if (instancing > 1) { + // trails + + uint stride = 2 + 1 + 1; //particles always uses this format + + uint trail_size = instancing; + + uint offset = trail_size * stride * gl_InstanceID; + + vec4 pcolor; + vec2 new_vertex; + { + uint boffset = offset + bone_attrib.x * stride; + new_vertex = (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.x; + pcolor = transforms.data[boffset + 2] * weight_attrib.x; + } + if (weight_attrib.y > 0.001) { + uint boffset = offset + bone_attrib.y * stride; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.y; + pcolor += transforms.data[boffset + 2] * weight_attrib.y; + } + if (weight_attrib.z > 0.001) { + uint boffset = offset + bone_attrib.z * stride; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.z; + pcolor += transforms.data[boffset + 2] * weight_attrib.z; + } + if (weight_attrib.w > 0.001) { + uint boffset = offset + bone_attrib.w * stride; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.w; + pcolor += transforms.data[boffset + 2] * weight_attrib.w; + } + + instance_custom = transforms.data[offset + 3]; + + vertex = new_vertex; + color *= pcolor; + } else*/ +#endif // USE_ATTRIBUTES +/* + { + if (instancing == 1) { + uint stride = 2; + { + if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) { + stride += 1; + } + if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { + stride += 1; + } + } + + uint offset = stride * gl_InstanceID; + + mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + offset += 2; + + if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) { + color *= transforms.data[offset]; + offset += 1; + } + + if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { + instance_custom = transforms.data[offset]; + } + + matrix = transpose(matrix); + model_matrix = model_matrix * matrix; + } + } +*/ +#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) + if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_PARTICLES)) { + //scale by texture size + vertex /= draw_data[draw_data_instance].color_texture_pixel_size; + } +#endif + +#ifdef USE_POINT_SIZE + float point_size = 1.0; +#endif + { +#CODE : VERTEX + } + +#ifdef USE_NINEPATCH + pixel_size_interp = abs(draw_data[draw_data_instance].dst_rect.zw) * vertex_base; +#endif + +#if !defined(SKIP_TRANSFORM_USED) + vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy; +#endif + + color_interp = color; + + if (use_pixel_snap) { + vertex = floor(vertex + 0.5); + // precision issue on some hardware creates artifacts within texture + // offset uv by a small amount to avoid + uv += 1e-5; + } + +#ifdef USE_ATTRIBUTES +#if 0 + if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_SKELETON) && bone_weights != vec4(0.0)) { //must be a valid bone + //skeleton transform + ivec4 bone_indicesi = ivec4(bone_indices); + + uvec2 tex_ofs = bone_indicesi.x * 2; + + mat2x4 m; + m = mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.x; + + tex_ofs = bone_indicesi.y * 2; + + m += mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.y; + + tex_ofs = bone_indicesi.z * 2; + + m += mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.z; + + tex_ofs = bone_indicesi.w * 2; + + m += mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.w; + + mat4 bone_matrix = skeleton_data.skeleton_transform * transpose(mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))) * skeleton_data.skeleton_transform_inverse; + + //outvec = bone_matrix * outvec; + } +#endif +#endif + + vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy; + + vertex_interp = vertex; + uv_interp = uv; + + gl_Position = screen_transform * vec4(vertex, 0.0, 1.0); + +#ifdef USE_POINT_SIZE + gl_PointSize = point_size; +#endif +} + +#[fragment] + +#include "canvas_uniforms_inc.glsl" +#include "stdlib_inc.glsl" + +uniform sampler2D atlas_texture; //texunit:-2 +uniform sampler2D shadow_atlas_texture; //texunit:-3 +uniform sampler2D screen_texture; //texunit:-4 +uniform sampler2D sdf_texture; //texunit:-5 +uniform sampler2D normal_texture; //texunit:-6 +uniform sampler2D specular_texture; //texunit:-7 + +uniform sampler2D color_texture; //texunit:0 + +in vec2 uv_interp; +in vec4 color_interp; +in vec2 vertex_interp; +flat in int draw_data_instance; + +#ifdef USE_NINEPATCH + +in vec2 pixel_size_interp; + +#endif + +layout(location = 0) out vec4 frag_color; + +#ifdef MATERIAL_UNIFORMS_USED +uniform MaterialUniforms{ +//ubo:4 + +#MATERIAL_UNIFORMS + +}; +#endif + +vec2 screen_uv_to_sdf(vec2 p_uv) { + return screen_to_sdf * p_uv; +} + +float texture_sdf(vec2 p_sdf) { + vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw; + float d = texture(sdf_texture, uv).r; + d *= SDF_MAX_LENGTH; + return d * tex_to_sdf; +} + +vec2 texture_sdf_normal(vec2 p_sdf) { + vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw; + + const float EPSILON = 0.001; + return normalize(vec2( + texture(sdf_texture, uv + vec2(EPSILON, 0.0)).r - texture(sdf_texture, uv - vec2(EPSILON, 0.0)).r, + texture(sdf_texture, uv + vec2(0.0, EPSILON)).r - texture(sdf_texture, uv - vec2(0.0, EPSILON)).r)); +} + +vec2 sdf_to_screen_uv(vec2 p_sdf) { + return p_sdf * sdf_to_screen; +} + +#GLOBALS + +#ifdef LIGHT_CODE_USED + +vec4 light_compute( + vec3 light_vertex, + vec3 light_position, + vec3 normal, + vec4 light_color, + float light_energy, + vec4 specular_shininess, + inout vec4 shadow_modulate, + vec2 screen_uv, + vec2 uv, + vec4 color, bool is_directional) { + vec4 light = vec4(0.0); + +#CODE : LIGHT + + return light; +} + +#endif + +#ifdef USE_NINEPATCH + +float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) { + float tex_size = 1.0 / tex_pixel_size; + + if (pixel < margin_begin) { + return pixel * tex_pixel_size; + } else if (pixel >= draw_size - margin_end) { + return (tex_size - (draw_size - pixel)) * tex_pixel_size; + } else { + if (!bool(draw_data[draw_data_instance].flags & FLAGS_NINEPACH_DRAW_CENTER)) { + draw_center--; + } + + // np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum. + if (np_repeat == 0) { // Stretch. + // Convert to ratio. + float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end); + // Scale to source texture. + return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size; + } else if (np_repeat == 1) { // Tile. + // Convert to offset. + float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end); + // Scale to source texture. + return (margin_begin + ofs) * tex_pixel_size; + } else if (np_repeat == 2) { // Tile Fit. + // Calculate scale. + float src_area = draw_size - margin_begin - margin_end; + float dst_area = tex_size - margin_begin - margin_end; + float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5)); + // Convert to ratio. + float ratio = (pixel - margin_begin) / src_area; + ratio = mod(ratio * scale, 1.0); + // Scale to source texture. + return (margin_begin + ratio * dst_area) * tex_pixel_size; + } else { // Shouldn't happen, but silences compiler warning. + return 0.0; + } + } +} + +#endif + +vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) { + float cNdotL = max(0.0, dot(normal, light_vec)); + + if (specular_shininess_used) { + //blinn + vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough + vec3 half_vec = normalize(view + light_vec); + + float cNdotV = max(dot(normal, view), 0.0); + float cNdotH = max(dot(normal, half_vec), 0.0); + float cVdotH = max(dot(view, half_vec), 0.0); + float cLdotH = max(dot(light_vec, half_vec), 0.0); + float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); + float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75); + + return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL; + } else { + return light_color * base_color * cNdotL; + } +} + +//float distance = length(shadow_pos); +vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv +#ifdef LIGHT_CODE_USED + , + vec3 shadow_modulate +#endif +) { + float shadow; + uint shadow_mode = light_data[light_base].flags & LIGHT_FLAGS_FILTER_MASK; + + if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) { + shadow = textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x; + } else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) { + vec4 shadow_pixel_size = vec4(light_data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0); + shadow = 0.0; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 2.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 2.0, 0.0).x; + shadow /= 5.0; + } else { //PCF13 + vec4 shadow_pixel_size = vec4(light_data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0); + shadow = 0.0; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 6.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 5.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 4.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 3.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 2.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 2.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 3.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 4.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 5.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 6.0, 0.0).x; + shadow /= 13.0; + } + + vec4 shadow_color = unpackUnorm4x8(light_data[light_base].shadow_color); +#ifdef LIGHT_CODE_USED + shadow_color.rgb *= shadow_modulate; +#endif + + shadow_color.a *= light_color.a; //respect light alpha + + return mix(light_color, shadow_color, shadow); +} + +void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) { + uint blend_mode = light_data[light_base].flags & LIGHT_FLAGS_BLEND_MASK; + + switch (blend_mode) { + case LIGHT_FLAGS_BLEND_MODE_ADD: { + color.rgb += light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_SUB: { + color.rgb -= light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_MIX: { + color.rgb = mix(color.rgb, light_color.rgb, light_color.a); + } break; + } +} + +float msdf_median(float r, float g, float b, float a) { + return min(max(min(r, g), min(max(r, g), b)), a); +} + +vec2 msdf_map(vec2 value, vec2 in_min, vec2 in_max, vec2 out_min, vec2 out_max) { + return out_min + (out_max - out_min) * (value - in_min) / (in_max - in_min); +} + +void main() { + vec4 color = color_interp; + vec2 uv = uv_interp; + vec2 vertex = vertex_interp; + +#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) + +#ifdef USE_NINEPATCH + + int draw_center = 2; + uv = vec2( + map_ninepatch_axis(pixel_size_interp.x, abs(draw_data[draw_data_instance].dst_rect.z), draw_data[draw_data_instance].color_texture_pixel_size.x, draw_data[draw_data_instance].ninepatch_margins.x, draw_data[draw_data_instance].ninepatch_margins.z, int(draw_data[draw_data_instance].flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center), + map_ninepatch_axis(pixel_size_interp.y, abs(draw_data[draw_data_instance].dst_rect.w), draw_data[draw_data_instance].color_texture_pixel_size.y, draw_data[draw_data_instance].ninepatch_margins.y, draw_data[draw_data_instance].ninepatch_margins.w, int(draw_data[draw_data_instance].flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center)); + + if (draw_center == 0) { + color.a = 0.0; + } + + uv = uv * draw_data[draw_data_instance].src_rect.zw + draw_data[draw_data_instance].src_rect.xy; //apply region if needed + +#endif + if (bool(draw_data[draw_data_instance].flags & FLAGS_CLIP_RECT_UV)) { + uv = clamp(uv, draw_data[draw_data_instance].src_rect.xy, draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw)); + } + +#endif + +#ifndef USE_PRIMITIVE + if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_MSDF)) { + float px_range = draw_data[draw_data_instance].ninepatch_margins.x; + float outline_thickness = draw_data[draw_data_instance].ninepatch_margins.y; + //float reserved1 = draw_data[draw_data_instance].ninepatch_margins.z; + //float reserved2 = draw_data[draw_data_instance].ninepatch_margins.w; + + vec4 msdf_sample = texture(color_texture, uv); + vec2 msdf_size = vec2(textureSize(color_texture, 0)); + vec2 dest_size = vec2(1.0) / fwidth(uv); + float px_size = max(0.5 * dot((vec2(px_range) / msdf_size), dest_size), 1.0); + float d = msdf_median(msdf_sample.r, msdf_sample.g, msdf_sample.b, msdf_sample.a) - 0.5; + + if (outline_thickness > 0) { + float cr = clamp(outline_thickness, 0.0, px_range / 2) / px_range; + float a = clamp((d + cr) * px_size, 0.0, 1.0); + color.a = a * color.a; + } else { + float a = clamp(d * px_size + 0.5, 0.0, 1.0); + color.a = a * color.a; + } + + } else { +#else + { +#endif + color *= texture(color_texture, uv); + } + + uint light_count = (draw_data[draw_data_instance].flags >> FLAGS_LIGHT_COUNT_SHIFT) & uint(0xF); //max 16 lights + bool using_light = light_count > uint(0) || directional_light_count > uint(0); + + vec3 normal; + +#if defined(NORMAL_USED) + bool normal_used = true; +#else + bool normal_used = false; +#endif + + if (normal_used || (using_light && bool(draw_data[draw_data_instance].flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) { + normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); + normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); + normal_used = true; + } else { + normal = vec3(0.0, 0.0, 1.0); + } + + vec4 specular_shininess; + +#if defined(SPECULAR_SHININESS_USED) + + bool specular_shininess_used = true; +#else + bool specular_shininess_used = false; +#endif + + if (specular_shininess_used || (using_light && normal_used && bool(draw_data[draw_data_instance].flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) { + specular_shininess = texture(specular_texture, uv); + specular_shininess *= unpackUnorm4x8(draw_data[draw_data_instance].specular_shininess); + specular_shininess_used = true; + } else { + specular_shininess = vec4(1.0); + } + +#if defined(SCREEN_UV_USED) + vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; +#else + vec2 screen_uv = vec2(0.0); +#endif + + vec3 light_vertex = vec3(vertex, 0.0); + vec2 shadow_vertex = vertex; + + { + float normal_map_depth = 1.0; + +#if defined(NORMAL_MAP_USED) + vec3 normal_map = vec3(0.0, 0.0, 1.0); + normal_used = true; +#endif + +#CODE : FRAGMENT + +#if defined(NORMAL_MAP_USED) + normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_map_depth); +#endif + } + + if (normal_used) { + //convert by item transform + normal.xy = mat2(normalize(draw_data[draw_data_instance].world_x), normalize(draw_data[draw_data_instance].world_y)) * normal.xy; + //convert by canvas transform + normal = normalize((canvas_normal_transform * vec4(normal, 0.0)).xyz); + } + + vec3 base_color = color.rgb; + if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_LIGHT_MASK)) { + color = vec4(0.0); //invisible by default due to using light mask + } + +#ifdef MODE_LIGHT_ONLY + color = vec4(0.0); +#else + color *= canvas_modulation; +#endif + +#if !defined(DISABLE_LIGHTING) && !defined(MODE_UNSHADED) + + for (uint i = uint(0); i < directional_light_count; i++) { + uint light_base = i; + + vec2 direction = light_data[light_base].position; + vec4 light_color = light_data[light_base].color; + +#ifdef LIGHT_CODE_USED + + vec4 shadow_modulate = vec4(1.0); + light_color = light_compute(light_vertex, vec3(direction, light_data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, true); +#else + + if (normal_used) { + vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_data[light_base].height)); + light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used); + } +#endif + + if (bool(light_data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { + vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_data[light_base].shadow_matrix[0], light_data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + + vec4 shadow_uv = vec4(shadow_pos.x, light_data[light_base].shadow_y_ofs, shadow_pos.y * light_data[light_base].shadow_zfar_inv, 1.0); + + light_color = light_shadow_compute(light_base, light_color, shadow_uv +#ifdef LIGHT_CODE_USED + , + shadow_modulate.rgb +#endif + ); + } + + light_blend_compute(light_base, light_color, color.rgb); + } + + // Positional Lights + + for (uint i = uint(0); i < MAX_LIGHTS_PER_ITEM; i++) { + if (i >= light_count) { + break; + } + uint light_base; + if (i < uint(8)) { + if (i < uint(4)) { + light_base = draw_data[draw_data_instance].lights.x; + } else { + light_base = draw_data[draw_data_instance].lights.y; + } + } else { + if (i < uint(12)) { + light_base = draw_data[draw_data_instance].lights.z; + } else { + light_base = draw_data[draw_data_instance].lights.w; + } + } + light_base >>= (i & uint(3)) * uint(8); + light_base &= uint(0xFF); + + vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_data[light_base].texture_matrix[0], light_data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + vec2 tex_uv_atlas = tex_uv * light_data[light_base].atlas_rect.zw + light_data[light_base].atlas_rect.xy; + vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0); + vec4 light_base_color = light_data[light_base].color; + +#ifdef LIGHT_CODE_USED + + vec4 shadow_modulate = vec4(1.0); + vec3 light_position = vec3(light_data[light_base].position, light_data[light_base].height); + + light_color.rgb *= light_base_color.rgb; + light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, false); +#else + + light_color.rgb *= light_base_color.rgb * light_base_color.a; + + if (normal_used) { + vec3 light_pos = vec3(light_data[light_base].position, light_data[light_base].height); + vec3 pos = light_vertex; + vec3 light_vec = normalize(light_pos - pos); + float cNdotL = max(0.0, dot(normal, light_vec)); + + light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used); + } +#endif + if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { + //if outside the light texture, light color is zero + light_color.a = 0.0; + } + + if (bool(light_data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { + vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_data[light_base].shadow_matrix[0], light_data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + + vec2 pos_norm = normalize(shadow_pos); + vec2 pos_abs = abs(pos_norm); + vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y); + vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot? + float tex_ofs; + float distance; + if (pos_rot.y > 0) { + if (pos_rot.x > 0) { + tex_ofs = pos_box.y * 0.125 + 0.125; + distance = shadow_pos.x; + } else { + tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125); + distance = shadow_pos.y; + } + } else { + if (pos_rot.x < 0) { + tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125); + distance = -shadow_pos.x; + } else { + tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125); + distance = -shadow_pos.y; + } + } + + distance *= light_data[light_base].shadow_zfar_inv; + + //float distance = length(shadow_pos); + vec4 shadow_uv = vec4(tex_ofs, light_data[light_base].shadow_y_ofs, distance, 1.0); + + light_color = light_shadow_compute(light_base, light_color, shadow_uv +#ifdef LIGHT_CODE_USED + , + shadow_modulate.rgb +#endif + ); + } + + light_blend_compute(light_base, light_color, color.rgb); + } +#endif // UNSHADED + + frag_color = color; +} diff --git a/drivers/gles3/shaders/canvas_shadow.glsl b/drivers/gles3/shaders/canvas_shadow.glsl new file mode 100644 index 0000000000..94485abd11 --- /dev/null +++ b/drivers/gles3/shaders/canvas_shadow.glsl @@ -0,0 +1,60 @@ +/* clang-format off */ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +precision highp float; +precision highp int; +#endif + +layout(location = 0) in highp vec3 vertex; + +uniform highp mat4 projection_matrix; +/* clang-format on */ +uniform highp mat4 light_matrix; +uniform highp mat4 model_matrix; +uniform highp float distance_norm; + +out highp vec4 position_interp; + +void main() { + gl_Position = projection_matrix * (light_matrix * (model_matrix * vec4(vertex, 1.0))); + position_interp = gl_Position; +} + +/* clang-format off */ +[fragment] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +#if defined(USE_HIGHP_PRECISION) +precision highp float; +precision highp int; +#else +precision mediump float; +precision mediump int; +#endif +#endif + +in highp vec4 position_interp; +/* clang-format on */ + +void main() { + highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias + +#ifdef USE_RGBA_SHADOWS + + highp vec4 comp = fract(depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0)); + comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0); + frag_color = comp; +#else + + frag_color = vec4(depth); +#endif +} diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl new file mode 100644 index 0000000000..e08a15e59d --- /dev/null +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -0,0 +1,120 @@ + +#define MAX_LIGHTS_PER_ITEM uint(16) + +#define M_PI 3.14159265359 + +#define SDF_MAX_LENGTH 16384.0 + +//1 means enabled, 2+ means trails in use +#define FLAGS_INSTANCING_MASK uint(0x7F) +#define FLAGS_INSTANCING_HAS_COLORS uint(1 << 7) +#define FLAGS_INSTANCING_HAS_CUSTOM_DATA uint(1 << 8) + +#define FLAGS_CLIP_RECT_UV uint(1 << 9) +#define FLAGS_TRANSPOSE_RECT uint(1 << 10) +#define FLAGS_USING_LIGHT_MASK uint(1 << 11) +#define FLAGS_NINEPACH_DRAW_CENTER uint(1 << 12) +#define FLAGS_USING_PARTICLES uint(1 << 13) + +#define FLAGS_NINEPATCH_H_MODE_SHIFT 16 +#define FLAGS_NINEPATCH_V_MODE_SHIFT 18 + +#define FLAGS_LIGHT_COUNT_SHIFT 20 + +#define FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 26) +#define FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 27) + +#define FLAGS_USE_MSDF uint(1 << 28) + +// must be always 128 bytes long +struct DrawData { + vec2 world_x; + vec2 world_y; + vec2 world_ofs; + vec2 color_texture_pixel_size; +#ifdef USE_PRIMITIVE + vec2 point_a; + vec2 point_b; + vec2 point_c; + vec2 uv_a; + vec2 uv_b; + vec2 uv_c; + uint color_a_rg; + uint color_a_ba; + uint color_b_rg; + uint color_b_ba; + uint color_c_rg; + uint color_c_ba; +#else + vec4 modulation; + vec4 ninepatch_margins; + vec4 dst_rect; //for built-in rect and UV + vec4 src_rect; + uint pad; + uint pad2; +#endif + uint flags; + uint specular_shininess; + uvec4 lights; +}; + +layout(std140) uniform GlobalVariableData { //ubo:1 + vec4 global_variables[MAX_GLOBAL_VARIABLES]; +}; + +layout(std140) uniform CanvasData { //ubo:0 + mat4 canvas_transform; + mat4 screen_transform; + mat4 canvas_normal_transform; + vec4 canvas_modulation; + vec2 screen_pixel_size; + float time; + bool use_pixel_snap; + + vec4 sdf_to_tex; + vec2 screen_to_sdf; + vec2 sdf_to_screen; + + uint directional_light_count; + float tex_to_sdf; + uint pad1; + uint pad2; +}; + +#define LIGHT_FLAGS_BLEND_MASK uint(3 << 16) +#define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16) +#define LIGHT_FLAGS_BLEND_MODE_SUB uint(1 << 16) +#define LIGHT_FLAGS_BLEND_MODE_MIX uint(2 << 16) +#define LIGHT_FLAGS_BLEND_MODE_MASK uint(3 << 16) +#define LIGHT_FLAGS_HAS_SHADOW uint(1 << 20) +#define LIGHT_FLAGS_FILTER_SHIFT 22 +#define LIGHT_FLAGS_FILTER_MASK uint(3 << 22) +#define LIGHT_FLAGS_SHADOW_NEAREST uint(0 << 22) +#define LIGHT_FLAGS_SHADOW_PCF5 uint(1 << 22) +#define LIGHT_FLAGS_SHADOW_PCF13 uint(2 << 22) + +struct Light { + mat2x4 texture_matrix; //light to texture coordinate matrix (transposed) + mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed) + vec4 color; + + uint shadow_color; // packed + uint flags; //index to light texture + float shadow_pixel_size; + float height; + + vec2 position; + float shadow_zfar_inv; + float shadow_y_ofs; + + vec4 atlas_rect; +}; + +layout(std140) uniform LightData { //ubo:2 + Light light_data[MAX_LIGHTS]; +}; + +layout(std140) uniform DrawDataInstances { //ubo:3 + + DrawData draw_data[MAX_DRAW_DATA_INSTANCES]; +}; diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl new file mode 100644 index 0000000000..62332a15a7 --- /dev/null +++ b/drivers/gles3/shaders/copy.glsl @@ -0,0 +1,204 @@ +/* clang-format off */ +#[modes] + +mode_default = +mode_cubemap = #define USE_CUBEMAP +mode_panorama = #define USE_PANORAMA +mode_copy_section = #define USE_COPY_SECTION +mode_asym_pano = #define USE_ASYM_PANO +mode_no_alpha = #define USE_NO_ALPHA +mode_custom_alpha = #define USE_CUSTOM_ALPHA +mode_multiplier = #define USE_MULTIPLIER +mode_sep_cbcr_texture = #define USE_SEP_CBCR_TEXTURE +mode_ycbcr_to_rgb = #define USE_YCBCR_TO_RGB + +#[specializations] + + +#[vertex] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +precision highp float; +precision highp int; +#endif + +layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ + +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) +layout(location = 4) in vec3 cube_in; +#else +layout(location = 4) in vec2 uv_in; +#endif + +layout(location = 5) in vec2 uv2_in; + +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) +out vec3 cube_interp; +#else +out vec2 uv_interp; +#endif +out vec2 uv2_interp; + +#ifdef USE_COPY_SECTION +uniform highp vec4 copy_section; +#elif defined(USE_DISPLAY_TRANSFORM) +uniform highp mat4 display_transform; +#endif + +void main() { +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) + cube_interp = cube_in; +#elif defined(USE_ASYM_PANO) + uv_interp = vertex_attrib.xy; +#else + uv_interp = uv_in; +#endif + + uv2_interp = uv2_in; + gl_Position = vertex_attrib; + +#ifdef USE_COPY_SECTION + uv_interp = copy_section.xy + uv_interp * copy_section.zw; + gl_Position.xy = (copy_section.xy + (gl_Position.xy * 0.5 + 0.5) * copy_section.zw) * 2.0 - 1.0; +#elif defined(USE_DISPLAY_TRANSFORM) + uv_interp = (display_transform * vec4(uv_in, 1.0, 1.0)).xy; +#endif +} + +/* clang-format off */ +#[fragment] + +#define M_PI 3.14159265359 + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +#if defined(USE_HIGHP_PRECISION) +precision highp float; +precision highp int; +#else +precision mediump float; +precision mediump int; +#endif +#endif + +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) +in vec3 cube_interp; +#else +in vec2 uv_interp; +#endif +/* clang-format on */ + +#ifdef USE_ASYM_PANO +uniform highp mat4 pano_transform; +uniform highp vec4 asym_proj; +#endif + +#ifdef USE_CUBEMAP +uniform samplerCube source_cube; // texunit:0 +#else +uniform sampler2D source; // texunit:0 +#endif + +#ifdef USE_SEP_CBCR_TEXTURE +uniform sampler2D CbCr; //texunit:1 +#endif + +in vec2 uv2_interp; + +#ifdef USE_MULTIPLIER +uniform float multiplier; +#endif + +#ifdef USE_CUSTOM_ALPHA +uniform float custom_alpha; +#endif + +#if defined(USE_PANORAMA) || defined(USE_ASYM_PANO) +uniform highp mat4 sky_transform; + +vec4 texturePanorama(sampler2D pano, vec3 normal) { + vec2 st = vec2( + atan(normal.x, normal.z), + acos(normal.y)); + + if (st.x < 0.0) + st.x += M_PI * 2.0; + + st /= vec2(M_PI * 2.0, M_PI); + + return texture(pano, st); +} + +#endif + +layout(location = 0) out vec4 frag_color; + +void main() { +#ifdef USE_PANORAMA + + vec3 cube_normal = normalize(cube_interp); + cube_normal.z = -cube_normal.z; + cube_normal = mat3(sky_transform) * cube_normal; + cube_normal.z = -cube_normal.z; + + vec4 color = texturePanorama(source, cube_normal); + +#elif defined(USE_ASYM_PANO) + + // When an asymmetrical projection matrix is used (applicable for stereoscopic rendering i.e. VR) we need to do this calculation per fragment to get a perspective correct result. + // Asymmetrical projection means the center of projection is no longer in the center of the screen but shifted. + // The Matrix[2][0] (= asym_proj.x) and Matrix[2][1] (= asym_proj.z) values are what provide the right shift in the image. + + vec3 cube_normal; + cube_normal.z = -1.0; + cube_normal.x = (cube_normal.z * (-uv_interp.x - asym_proj.x)) / asym_proj.y; + cube_normal.y = (cube_normal.z * (-uv_interp.y - asym_proj.z)) / asym_proj.a; + cube_normal = mat3(sky_transform) * mat3(pano_transform) * cube_normal; + cube_normal.z = -cube_normal.z; + + vec4 color = texturePanorama(source, normalize(cube_normal.xyz)); + +#elif defined(USE_CUBEMAP) + vec4 color = texture(source_cube, normalize(cube_interp)); +#elif defined(USE_SEP_CBCR_TEXTURE) + vec4 color; + color.r = texture(source, uv_interp).r; + color.gb = texture(CbCr, uv_interp).rg - vec2(0.5, 0.5); + color.a = 1.0; +#else + vec4 color = texture(source, uv_interp); +#endif + +#ifdef USE_YCBCR_TO_RGB + // YCbCr -> RGB conversion + + // Using BT.601, which is the standard for SDTV is provided as a reference + color.rgb = mat3( + vec3(1.00000, 1.00000, 1.00000), + vec3(0.00000, -0.34413, 1.77200), + vec3(1.40200, -0.71414, 0.00000)) * + color.rgb; +#endif + +#ifdef USE_NO_ALPHA + color.a = 1.0; +#endif + +#ifdef USE_CUSTOM_ALPHA + color.a = custom_alpha; +#endif + +#ifdef USE_MULTIPLIER + color.rgb *= multiplier; +#endif + + frag_color = color; +} diff --git a/drivers/gles3/shaders/cube_to_dp.glsl b/drivers/gles3/shaders/cube_to_dp.glsl new file mode 100644 index 0000000000..2384529a89 --- /dev/null +++ b/drivers/gles3/shaders/cube_to_dp.glsl @@ -0,0 +1,100 @@ +/* clang-format off */ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif + +layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ +layout(location = 4) in vec2 uv_in; + +out vec2 uv_interp; + +void main() { + uv_interp = uv_in; + gl_Position = vertex_attrib; +} + +/* clang-format off */ +[fragment] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +#if defined(USE_HIGHP_PRECISION) +precision highp float; +precision highp int; +#else +precision mediump float; +precision mediump int; +#endif +#endif + +uniform highp samplerCube source_cube; //texunit:0 +/* clang-format on */ +in vec2 uv_interp; + +uniform bool z_flip; +uniform highp float z_far; +uniform highp float z_near; +uniform highp float bias; + +void main() { + highp vec3 normal = vec3(uv_interp * 2.0 - 1.0, 0.0); + /* + if (z_flip) { + normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y)); + } else { + normal.z = -0.5 + 0.5 * ((normal.x * normal.x) + (normal.y * normal.y)); + } + */ + + //normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); + //normal.xy *= 1.0 + normal.z; + + normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y)); + normal = normalize(normal); + /* + normal.z = 0.5; + normal = normalize(normal); + */ + + if (!z_flip) { + normal.z = -normal.z; + } + + //normal = normalize(vec3( uv_interp * 2.0 - 1.0, 1.0 )); + float depth = textureCube(source_cube, normal).r; + + // absolute values for direction cosines, bigger value equals closer to basis axis + vec3 unorm = abs(normal); + + if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) { + // x code + unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0); + } else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) { + // y code + unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0); + } else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) { + // z code + unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); + } else { + // oh-no we messed up code + // has to be + unorm = vec3(1.0, 0.0, 0.0); + } + + float depth_fix = 1.0 / dot(normal, unorm); + + depth = 2.0 * depth - 1.0; + float linear_depth = 2.0 * z_near * z_far / (z_far + z_near - depth * (z_far - z_near)); + gl_FragDepth = (linear_depth * depth_fix + bias) / z_far; +} diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl new file mode 100644 index 0000000000..2081abfef6 --- /dev/null +++ b/drivers/gles3/shaders/cubemap_filter.glsl @@ -0,0 +1,214 @@ +/* clang-format off */ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +precision highp float; +precision highp int; +#endif + +layout(location = 0) in highp vec2 vertex; +/* clang-format on */ +layout(location = 4) in highp vec2 uv; + +out highp vec2 uv_interp; + +void main() { + uv_interp = uv; + gl_Position = vec4(vertex, 0, 1); +} + +/* clang-format off */ +[fragment] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +#if defined(USE_HIGHP_PRECISION) +precision highp float; +precision highp int; +#else +precision mediump float; +precision mediump int; +#endif + +#endif + +#ifdef USE_SOURCE_PANORAMA +uniform sampler2D source_panorama; //texunit:0 +#else +uniform samplerCube source_cube; //texunit:0 +#endif +/* clang-format on */ + +uniform int face_id; +uniform float roughness; +in highp vec2 uv_interp; + +uniform sampler2D radical_inverse_vdc_cache; // texunit:1 + +#define M_PI 3.14159265359 + +#ifdef LOW_QUALITY + +#define SAMPLE_COUNT 64 + +#else + +#define SAMPLE_COUNT 512 + +#endif + +#ifdef USE_SOURCE_PANORAMA + +vec4 texturePanorama(sampler2D pano, vec3 normal) { + vec2 st = vec2( + atan(normal.x, normal.z), + acos(normal.y)); + + if (st.x < 0.0) + st.x += M_PI * 2.0; + + st /= vec2(M_PI * 2.0, M_PI); + + return textureLod(pano, st, 0.0); +} + +#endif + +vec3 texelCoordToVec(vec2 uv, int faceID) { + mat3 faceUvVectors[6]; + + // -x + faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0); // u -> +z + faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face + + // +x + faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z + faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0); // +x face + + // -y + faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z + faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face + + // +y + faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0); // v -> +z + faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0); // +y face + + // -z + faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x + faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face + + // +z + faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0); // +z face + + // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. + vec3 result; + for (int i = 0; i < 6; i++) { + if (i == faceID) { + result = (faceUvVectors[i][0] * uv.x) + (faceUvVectors[i][1] * uv.y) + faceUvVectors[i][2]; + break; + } + } + return normalize(result); +} + +vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { + float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] + + // Compute distribution direction + float Phi = 2.0 * M_PI * Xi.x; + float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y)); + float SinTheta = sqrt(1.0 - CosTheta * CosTheta); + + // Convert to spherical direction + vec3 H; + H.x = SinTheta * cos(Phi); + H.y = SinTheta * sin(Phi); + H.z = CosTheta; + + vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 TangentX = normalize(cross(UpVector, N)); + vec3 TangentY = cross(N, TangentX); + + // Tangent to world space + return TangentX * H.x + TangentY * H.y + N * H.z; +} + +float radical_inverse_VdC(int i) { + return texture(radical_inverse_vdc_cache, vec2(float(i) / 512.0, 0.0)).x; +} + +vec2 Hammersley(int i, int N) { + return vec2(float(i) / float(N), radical_inverse_VdC(i)); +} + +uniform bool z_flip; + +layout(location = 0) out vec4 frag_color; + +void main() { + vec3 color = vec3(0.0); + + vec2 uv = (uv_interp * 2.0) - 1.0; + vec3 N = texelCoordToVec(uv, face_id); + +#ifdef USE_DIRECT_WRITE + +#ifdef USE_SOURCE_PANORAMA + + frag_color = vec4(texturePanorama(source_panorama, N).rgb, 1.0); +#else + + frag_color = vec4(textureCube(source_cube, N).rgb, 1.0); +#endif //USE_SOURCE_PANORAMA + +#else + + vec4 sum = vec4(0.0); + + for (int sample_num = 0; sample_num < SAMPLE_COUNT; sample_num++) { + vec2 xi = Hammersley(sample_num, SAMPLE_COUNT); + + vec3 H = ImportanceSampleGGX(xi, roughness, N); + vec3 V = N; + vec3 L = (2.0 * dot(V, H) * H - V); + + float NdotL = clamp(dot(N, L), 0.0, 1.0); + + if (NdotL > 0.0) { + +#ifdef USE_SOURCE_PANORAMA + vec3 val = texturePanorama(source_panorama, L).rgb; +#else + vec3 val = textureCubeLod(source_cube, L, 0.0).rgb; +#endif + //mix using Linear, to approximate high end back-end + val = mix(pow((val + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), val * (1.0 / 12.92), vec3(lessThan(val, vec3(0.04045)))); + + sum.rgb += val * NdotL; + + sum.a += NdotL; + } + } + + sum /= sum.a; + + vec3 a = vec3(0.055); + sum.rgb = mix((vec3(1.0) + a) * pow(sum.rgb, vec3(1.0 / 2.4)) - a, 12.92 * sum.rgb, vec3(lessThan(sum.rgb, vec3(0.0031308)))); + + frag_color = vec4(sum.rgb, 1.0); +#endif +} diff --git a/drivers/gles3/shaders/effect_blur.glsl b/drivers/gles3/shaders/effect_blur.glsl new file mode 100644 index 0000000000..c9184cca77 --- /dev/null +++ b/drivers/gles3/shaders/effect_blur.glsl @@ -0,0 +1,291 @@ +/* clang-format off */ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +precision highp float; +precision highp int; +#endif + +layout(location = 0) in vec2 vertex_attrib; +/* clang-format on */ +layout(location = 4) in vec2 uv_in; + +out vec2 uv_interp; + +#ifdef USE_BLUR_SECTION + +uniform vec4 blur_section; + +#endif + +void main() { + uv_interp = uv_in; + gl_Position = vec4(vertex_attrib, 0.0, 1.0); +#ifdef USE_BLUR_SECTION + + uv_interp = blur_section.xy + uv_interp * blur_section.zw; + gl_Position.xy = (blur_section.xy + (gl_Position.xy * 0.5 + 0.5) * blur_section.zw) * 2.0 - 1.0; +#endif +} + +/* clang-format off */ +[fragment] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +#if defined(USE_HIGHP_PRECISION) +precision highp float; +precision highp int; +#else +precision mediump float; +precision mediump int; +#endif +#endif + +in vec2 uv_interp; +/* clang-format on */ +uniform sampler2D source_color; //texunit:0 + +uniform float lod; +uniform vec2 pixel_size; + +#if defined(GLOW_GAUSSIAN_HORIZONTAL) || defined(GLOW_GAUSSIAN_VERTICAL) + +uniform float glow_strength; + +#endif + +#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR) + +#ifdef USE_GLES_OVER_GL +#ifdef DOF_QUALITY_LOW +const int dof_kernel_size = 5; +const int dof_kernel_from = 2; +const float dof_kernel[5] = float[](0.153388, 0.221461, 0.250301, 0.221461, 0.153388); +#endif + +#ifdef DOF_QUALITY_MEDIUM +const int dof_kernel_size = 11; +const int dof_kernel_from = 5; +const float dof_kernel[11] = float[](0.055037, 0.072806, 0.090506, 0.105726, 0.116061, 0.119726, 0.116061, 0.105726, 0.090506, 0.072806, 0.055037); + +#endif + +#ifdef DOF_QUALITY_HIGH +const int dof_kernel_size = 21; +const int dof_kernel_from = 10; +const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.046421, 0.050582, 0.054261, 0.057307, 0.059587, 0.060998, 0.061476, 0.060998, 0.059587, 0.057307, 0.054261, 0.050582, 0.046421, 0.041944, 0.037311, 0.032676, 0.028174); +#endif +#endif + +uniform sampler2D dof_source_depth; //texunit:1 +uniform float dof_begin; +uniform float dof_end; +uniform vec2 dof_dir; +uniform float dof_radius; + +#endif + +#ifdef GLOW_FIRST_PASS + +uniform highp float luminance_cap; + +uniform float glow_bloom; +uniform float glow_hdr_threshold; +uniform float glow_hdr_scale; + +#endif + +uniform float camera_z_far; +uniform float camera_z_near; + +layout(location = 0) out vec4 frag_color; + +void main() { +#ifdef GLOW_GAUSSIAN_HORIZONTAL + vec2 pix_size = pixel_size; + pix_size *= 0.5; //reading from larger buffer, so use more samples + vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pix_size, lod) * 0.174938; + color += textureLod(source_color, uv_interp + vec2(1.0, 0.0) * pix_size, lod) * 0.165569; + color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.140367; + color += textureLod(source_color, uv_interp + vec2(3.0, 0.0) * pix_size, lod) * 0.106595; + color += textureLod(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size, lod) * 0.165569; + color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.140367; + color += textureLod(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size, lod) * 0.106595; + color *= glow_strength; + frag_color = color; +#endif + +#ifdef GLOW_GAUSSIAN_VERTICAL + vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pixel_size, lod) * 0.288713; + color += textureLod(source_color, uv_interp + vec2(0.0, 1.0) * pixel_size, lod) * 0.233062; + color += textureLod(source_color, uv_interp + vec2(0.0, 2.0) * pixel_size, lod) * 0.122581; + color += textureLod(source_color, uv_interp + vec2(0.0, -1.0) * pixel_size, lod) * 0.233062; + color += textureLod(source_color, uv_interp + vec2(0.0, -2.0) * pixel_size, lod) * 0.122581; + color *= glow_strength; + frag_color = color; +#endif + +#ifndef USE_GLES_OVER_GL +#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR) + +#ifdef DOF_QUALITY_LOW + const int dof_kernel_size = 5; + const int dof_kernel_from = 2; + float dof_kernel[5]; + dof_kernel[0] = 0.153388; + dof_kernel[1] = 0.221461; + dof_kernel[2] = 0.250301; + dof_kernel[3] = 0.221461; + dof_kernel[4] = 0.153388; +#endif + +#ifdef DOF_QUALITY_MEDIUM + const int dof_kernel_size = 11; + const int dof_kernel_from = 5; + float dof_kernel[11]; + dof_kernel[0] = 0.055037; + dof_kernel[1] = 0.072806; + dof_kernel[2] = 0.090506; + dof_kernel[3] = 0.105726; + dof_kernel[4] = 0.116061; + dof_kernel[5] = 0.119726; + dof_kernel[6] = 0.116061; + dof_kernel[7] = 0.105726; + dof_kernel[8] = 0.090506; + dof_kernel[9] = 0.072806; + dof_kernel[10] = 0.055037; +#endif + +#ifdef DOF_QUALITY_HIGH + const int dof_kernel_size = 21; + const int dof_kernel_from = 10; + float dof_kernel[21]; + dof_kernel[0] = 0.028174; + dof_kernel[1] = 0.032676; + dof_kernel[2] = 0.037311; + dof_kernel[3] = 0.041944; + dof_kernel[4] = 0.046421; + dof_kernel[5] = 0.050582; + dof_kernel[6] = 0.054261; + dof_kernel[7] = 0.057307; + dof_kernel[8] = 0.059587; + dof_kernel[9] = 0.060998; + dof_kernel[10] = 0.061476; + dof_kernel[11] = 0.060998; + dof_kernel[12] = 0.059587; + dof_kernel[13] = 0.057307; + dof_kernel[14] = 0.054261; + dof_kernel[15] = 0.050582; + dof_kernel[16] = 0.046421; + dof_kernel[17] = 0.041944; + dof_kernel[18] = 0.037311; + dof_kernel[19] = 0.032676; + dof_kernel[20] = 0.028174; +#endif +#endif +#endif //!USE_GLES_OVER_GL + +#ifdef DOF_FAR_BLUR + + vec4 color_accum = vec4(0.0); + + float depth = textureLod(dof_source_depth, uv_interp, 0.0).r; + depth = depth * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + depth = ((depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0; +#else + depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near)); +#endif + + float amount = smoothstep(dof_begin, dof_end, depth); + float k_accum = 0.0; + + for (int i = 0; i < dof_kernel_size; i++) { + int int_ofs = i - dof_kernel_from; + vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * amount * dof_radius; + + float tap_k = dof_kernel[i]; + + float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r; + tap_depth = tap_depth * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0; +#else + tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near)); +#endif + float tap_amount = int_ofs == 0 ? 1.0 : smoothstep(dof_begin, dof_end, tap_depth); + tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect + + vec4 tap_color = textureLod(source_color, tap_uv, 0.0) * tap_k; + + k_accum += tap_k * tap_amount; + color_accum += tap_color * tap_amount; + } + + if (k_accum > 0.0) { + color_accum /= k_accum; + } + + frag_color = color_accum; ///k_accum; + +#endif + +#ifdef DOF_NEAR_BLUR + + vec4 color_accum = vec4(0.0); + + float max_accum = 0.0; + + for (int i = 0; i < dof_kernel_size; i++) { + int int_ofs = i - dof_kernel_from; + vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * dof_radius; + float ofs_influence = max(0.0, 1.0 - abs(float(int_ofs)) / float(dof_kernel_from)); + + float tap_k = dof_kernel[i]; + + vec4 tap_color = textureLod(source_color, tap_uv, 0.0); + + float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r; + tap_depth = tap_depth * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0; +#else + tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near)); +#endif + float tap_amount = 1.0 - smoothstep(dof_end, dof_begin, tap_depth); + tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect + +#ifdef DOF_NEAR_FIRST_TAP + + tap_color.a = 1.0 - smoothstep(dof_end, dof_begin, tap_depth); + +#endif + + max_accum = max(max_accum, tap_amount * ofs_influence); + + color_accum += tap_color * tap_k; + } + + color_accum.a = max(color_accum.a, sqrt(max_accum)); + + frag_color = color_accum; + +#endif + +#ifdef GLOW_FIRST_PASS + + float luminance = max(frag_color.r, max(frag_color.g, frag_color.b)); + float feedback = max(smoothstep(glow_hdr_threshold, glow_hdr_threshold + glow_hdr_scale, luminance), glow_bloom); + + frag_color = min(frag_color * feedback, vec4(luminance_cap)); + +#endif +} diff --git a/drivers/gles3/shaders/lens_distorted.glsl b/drivers/gles3/shaders/lens_distorted.glsl new file mode 100644 index 0000000000..3aaf1050e5 --- /dev/null +++ b/drivers/gles3/shaders/lens_distorted.glsl @@ -0,0 +1,86 @@ +/* clang-format off */ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +precision highp float; +precision highp int; +#endif + +layout(location = 0) in highp vec2 vertex; +/* clang-format on */ + +uniform vec2 offset; +uniform vec2 scale; + +out vec2 uv_interp; + +void main() { + uv_interp = vertex.xy * 2.0 - 1.0; + + vec2 v = vertex.xy * scale + offset; + gl_Position = vec4(v, 0.0, 1.0); +} + +/* clang-format off */ +[fragment] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +#if defined(USE_HIGHP_PRECISION) +precision highp float; +precision highp int; +#else +precision mediump float; +precision mediump int; +#endif +#endif + +uniform sampler2D source; //texunit:0 +/* clang-format on */ + +uniform vec2 eye_center; +uniform float k1; +uniform float k2; +uniform float upscale; +uniform float aspect_ratio; + +in vec2 uv_interp; + +layout(location = 0) out vec4 frag_color; + +void main() { + vec2 coords = uv_interp; + vec2 offset = coords - eye_center; + + // take aspect ratio into account + offset.y /= aspect_ratio; + + // distort + vec2 offset_sq = offset * offset; + float radius_sq = offset_sq.x + offset_sq.y; + float radius_s4 = radius_sq * radius_sq; + float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4); + offset *= distortion_scale; + + // reapply aspect ratio + offset.y *= aspect_ratio; + + // add our eye center back in + coords = offset + eye_center; + coords /= upscale; + + // and check our color + if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) { + frag_color = vec4(0.0, 0.0, 0.0, 1.0); + } else { + coords = (coords + vec2(1.0)) / vec2(2.0); + frag_color = texture(source, coords); + } +} diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl new file mode 100644 index 0000000000..ebb00e81d0 --- /dev/null +++ b/drivers/gles3/shaders/scene.glsl @@ -0,0 +1,2153 @@ +/* clang-format off */ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +precision highp float; +precision highp int; +#endif + +#define SHADER_IS_SRGB true //TODO remove + +#define M_PI 3.14159265359 + +// +// attributes +// + +layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ +layout(location = 1) in vec3 normal_attrib; + +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +layout(location = 2) in vec4 tangent_attrib; +#endif + +#if defined(ENABLE_COLOR_INTERP) +layout(location = 3) in vec4 color_attrib; +#endif + +#if defined(ENABLE_UV_INTERP) +layout(location = 4) in vec2 uv_attrib; +#endif + +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) +layout(location = 5) in vec2 uv2_attrib; +#endif + +#ifdef USE_SKELETON + +#ifdef USE_SKELETON_SOFTWARE + +layout(location = 13) in highp vec4 bone_transform_row_0; +layout(location = 14) in highp vec4 bone_transform_row_1; +layout(location = 15) in highp vec4 bone_transform_row_2; + +#else + +layout(location = 6) in vec4 bone_ids; +layout(location = 7) in highp vec4 bone_weights; + +uniform highp sampler2D bone_transforms; // texunit:-1 +uniform ivec2 skeleton_texture_size; + +#endif + +#endif + +#ifdef USE_INSTANCING + +layout(location = 8) in highp vec4 instance_xform_row_0; +layout(location = 9) in highp vec4 instance_xform_row_1; +layout(location = 10) in highp vec4 instance_xform_row_2; + +layout(location = 11) in highp vec4 instance_color; +layout(location = 12) in highp vec4 instance_custom_data; + +#endif + +// +// uniforms +// + +uniform highp mat4 inv_view_matrix; +uniform highp mat4 view_matrix; +uniform highp mat4 projection_matrix; +uniform highp mat4 projection_inverse_matrix; + +uniform highp mat4 world_transform; + +uniform highp float time; + +uniform highp vec2 viewport_size; + +#ifdef RENDER_DEPTH +uniform float light_bias; +uniform float light_normal_bias; +#endif + +// +// varyings +// + +#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS) +out highp vec4 position_interp; +#endif + +out highp vec3 vertex_interp; +out vec3 normal_interp; + +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +out vec3 tangent_interp; +out vec3 binormal_interp; +#endif + +#if defined(ENABLE_COLOR_INTERP) +out vec4 color_interp; +#endif + +#if defined(ENABLE_UV_INTERP) +out vec2 uv_interp; +#endif + +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) +out vec2 uv2_interp; +#endif + +/* clang-format off */ + +VERTEX_SHADER_GLOBALS + +/* clang-format on */ + +#ifdef RENDER_DEPTH_DUAL_PARABOLOID + +out highp float dp_clip; +uniform highp float shadow_dual_paraboloid_render_zfar; +uniform highp float shadow_dual_paraboloid_render_side; + +#endif + +#if defined(USE_SHADOW) && defined(USE_LIGHTING) + +uniform highp mat4 light_shadow_matrix; +out highp vec4 shadow_coord; + +#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4) +uniform highp mat4 light_shadow_matrix2; +out highp vec4 shadow_coord2; +#endif + +#if defined(LIGHT_USE_PSSM4) + +uniform highp mat4 light_shadow_matrix3; +uniform highp mat4 light_shadow_matrix4; +out highp vec4 shadow_coord3; +out highp vec4 shadow_coord4; + +#endif + +#endif + +#if defined(USE_VERTEX_LIGHTING) && defined(USE_LIGHTING) + +out highp vec3 diffuse_interp; +out highp vec3 specular_interp; + +// general for all lights +uniform highp vec4 light_color; +uniform highp vec4 shadow_color; +uniform highp float light_specular; + +// directional +uniform highp vec3 light_direction; + +// omni +uniform highp vec3 light_position; + +uniform highp float light_range; +uniform highp float light_attenuation; + +// spot +uniform highp float light_spot_attenuation; +uniform highp float light_spot_range; +uniform highp float light_spot_angle; + +void light_compute( + vec3 N, + vec3 L, + vec3 V, + vec3 light_color, + vec3 attenuation, + float roughness) { +//this makes lights behave closer to linear, but then addition of lights looks bad +//better left disabled + +//#define SRGB_APPROX(m_var) m_var = pow(m_var,0.4545454545); +/* +#define SRGB_APPROX(m_var) {\ + float S1 = sqrt(m_var);\ + float S2 = sqrt(S1);\ + float S3 = sqrt(S2);\ + m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\ + } +*/ +#define SRGB_APPROX(m_var) + + float NdotL = dot(N, L); + float cNdotL = max(NdotL, 0.0); // clamped NdotL + float NdotV = dot(N, V); + float cNdotV = max(NdotV, 0.0); + +#if defined(DIFFUSE_OREN_NAYAR) + vec3 diffuse_brdf_NL; +#else + float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance +#endif + +#if defined(DIFFUSE_LAMBERT_WRAP) + // energy conserving lambert wrap shader + diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); + +#elif defined(DIFFUSE_OREN_NAYAR) + + { + // see http://mimosa-pudica.net/improved-oren-nayar.html + float LdotV = dot(L, V); + + float s = LdotV - NdotL * NdotV; + float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); + + float sigma2 = roughness * roughness; // TODO: this needs checking + vec3 A = 1.0 + sigma2 * (-0.5 / (sigma2 + 0.33) + 0.17 * diffuse_color / (sigma2 + 0.13)); + float B = 0.45 * sigma2 / (sigma2 + 0.09); + + diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI); + } +#else + // lambert by default for everything else + diffuse_brdf_NL = cNdotL * (1.0 / M_PI); +#endif + + SRGB_APPROX(diffuse_brdf_NL) + + diffuse_interp += light_color * diffuse_brdf_NL * attenuation; + + if (roughness > 0.0) { + // D + float specular_brdf_NL = 0.0; + +#if !defined(SPECULAR_DISABLED) + //normalized blinn always unless disabled + vec3 H = normalize(V + L); + float cNdotH = max(dot(N, H), 0.0); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess) * cNdotL; + blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); + specular_brdf_NL = blinn; +#endif + + SRGB_APPROX(specular_brdf_NL) + specular_interp += specular_brdf_NL * light_color * attenuation * (1.0 / M_PI); + } +} + +#endif + +#ifdef USE_VERTEX_LIGHTING + +#ifdef USE_REFLECTION_PROBE1 + +uniform highp mat4 refprobe1_local_matrix; +out mediump vec4 refprobe1_reflection_normal_blend; +uniform highp vec3 refprobe1_box_extents; + +#ifndef USE_LIGHTMAP +out mediump vec3 refprobe1_ambient_normal; +#endif + +#endif //reflection probe1 + +#ifdef USE_REFLECTION_PROBE2 + +uniform highp mat4 refprobe2_local_matrix; +out mediump vec4 refprobe2_reflection_normal_blend; +uniform highp vec3 refprobe2_box_extents; + +#ifndef USE_LIGHTMAP +out mediump vec3 refprobe2_ambient_normal; +#endif + +#endif //reflection probe2 + +#endif //vertex lighting for refprobes + +#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + +out vec4 fog_interp; + +uniform mediump vec4 fog_color_base; +#ifdef LIGHT_MODE_DIRECTIONAL +uniform mediump vec4 fog_sun_color_amount; +#endif + +uniform bool fog_transmit_enabled; +uniform mediump float fog_transmit_curve; + +#ifdef FOG_DEPTH_ENABLED +uniform highp float fog_depth_begin; +uniform mediump float fog_depth_curve; +uniform mediump float fog_max_distance; +#endif + +#ifdef FOG_HEIGHT_ENABLED +uniform highp float fog_height_min; +uniform highp float fog_height_max; +uniform mediump float fog_height_curve; +#endif + +#endif //fog + +void main() { + highp vec4 vertex = vertex_attrib; + + mat4 model_matrix = world_transform; + +#ifdef USE_INSTANCING + { + highp mat4 m = mat4( + instance_xform_row_0, + instance_xform_row_1, + instance_xform_row_2, + vec4(0.0, 0.0, 0.0, 1.0)); + model_matrix = model_matrix * transpose(m); + } + +#endif + + vec3 normal = normal_attrib; + +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + vec3 tangent = tangent_attrib.xyz; + float binormalf = tangent_attrib.a; + vec3 binormal = normalize(cross(normal, tangent) * binormalf); +#endif + +#if defined(ENABLE_COLOR_INTERP) + color_interp = color_attrib; +#ifdef USE_INSTANCING + color_interp *= instance_color; +#endif +#endif + +#if defined(ENABLE_UV_INTERP) + uv_interp = uv_attrib; +#endif + +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) + uv2_interp = uv2_attrib; +#endif + +#if defined(OVERRIDE_POSITION) + highp vec4 position; +#endif + +#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) + vertex = model_matrix * vertex; + normal = normalize((model_matrix * vec4(normal, 0.0)).xyz); +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + + tangent = normalize((model_matrix * vec4(tangent, 0.0)).xyz); + binormal = normalize((model_matrix * vec4(binormal, 0.0)).xyz); +#endif +#endif + +#ifdef USE_SKELETON + + highp mat4 bone_transform = mat4(0.0); + +#ifdef USE_SKELETON_SOFTWARE + // passing the transform as attributes + + bone_transform[0] = vec4(bone_transform_row_0.x, bone_transform_row_1.x, bone_transform_row_2.x, 0.0); + bone_transform[1] = vec4(bone_transform_row_0.y, bone_transform_row_1.y, bone_transform_row_2.y, 0.0); + bone_transform[2] = vec4(bone_transform_row_0.z, bone_transform_row_1.z, bone_transform_row_2.z, 0.0); + bone_transform[3] = vec4(bone_transform_row_0.w, bone_transform_row_1.w, bone_transform_row_2.w, 1.0); + +#else + // look up transform from the "pose texture" + { + for (int i = 0; i < 4; i++) { + ivec2 tex_ofs = ivec2(int(bone_ids[i]) * 3, 0); + + highp mat4 b = mat4( + texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(0, 0)), + texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(1, 0)), + texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(2, 0)), + vec4(0.0, 0.0, 0.0, 1.0)); + + bone_transform += transpose(b) * bone_weights[i]; + } + } + +#endif + + model_matrix = model_matrix * bone_transform; + +#endif + +#ifdef USE_INSTANCING + vec4 instance_custom = instance_custom_data; +#else + vec4 instance_custom = vec4(0.0); + +#endif + + mat4 local_projection_matrix = projection_matrix; + + mat4 modelview = view_matrix * model_matrix; + float roughness = 1.0; + +#define projection_matrix local_projection_matrix +#define world_transform model_matrix + + float point_size = 1.0; + + { + /* clang-format off */ + +VERTEX_SHADER_CODE + + /* clang-format on */ + } + + gl_PointSize = point_size; + vec4 outvec = vertex; + + // use local coordinates +#if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED) + vertex = modelview * vertex; + normal = normalize((modelview * vec4(normal, 0.0)).xyz); + +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + tangent = normalize((modelview * vec4(tangent, 0.0)).xyz); + binormal = normalize((modelview * vec4(binormal, 0.0)).xyz); +#endif +#endif + +#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) + vertex = view_matrix * vertex; + normal = normalize((view_matrix * vec4(normal, 0.0)).xyz); +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + tangent = normalize((view_matrix * vec4(tangent, 0.0)).xyz); + binormal = normalize((view_matrix * vec4(binormal, 0.0)).xyz); +#endif +#endif + + vertex_interp = vertex.xyz; + normal_interp = normal; + +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + tangent_interp = tangent; + binormal_interp = binormal; +#endif + +#ifdef RENDER_DEPTH + +#ifdef RENDER_DEPTH_DUAL_PARABOLOID + + vertex_interp.z *= shadow_dual_paraboloid_render_side; + normal_interp.z *= shadow_dual_paraboloid_render_side; + + dp_clip = vertex_interp.z; //this attempts to avoid noise caused by objects sent to the other parabolloid side due to bias + + //for dual paraboloid shadow mapping, this is the fastest but least correct way, as it curves straight edges + + highp vec3 vtx = vertex_interp + normalize(vertex_interp) * light_bias; + highp float distance = length(vtx); + vtx = normalize(vtx); + vtx.xy /= 1.0 - vtx.z; + vtx.z = (distance / shadow_dual_paraboloid_render_zfar); + vtx.z = vtx.z * 2.0 - 1.0; + + vertex_interp = vtx; + +#else + float z_ofs = light_bias; + z_ofs += (1.0 - abs(normal_interp.z)) * light_normal_bias; + + vertex_interp.z -= z_ofs; +#endif //dual parabolloid + +#endif //depth + +//vertex lighting +#if defined(USE_VERTEX_LIGHTING) && defined(USE_LIGHTING) + //vertex shaded version of lighting (more limited) + vec3 L; + vec3 light_att; + +#ifdef LIGHT_MODE_OMNI + vec3 light_vec = light_position - vertex_interp; + float light_length = length(light_vec); + + float normalized_distance = light_length / light_range; + + if (normalized_distance < 1.0) { + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); + + vec3 attenuation = vec3(omni_attenuation); + light_att = vec3(omni_attenuation); + } else { + light_att = vec3(0.0); + } + + L = normalize(light_vec); + +#endif + +#ifdef LIGHT_MODE_SPOT + + vec3 light_rel_vec = light_position - vertex_interp; + float light_length = length(light_rel_vec); + float normalized_distance = light_length / light_range; + + if (normalized_distance < 1.0) { + float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); + vec3 spot_dir = light_direction; + + float spot_cutoff = light_spot_angle; + + float angle = dot(-normalize(light_rel_vec), spot_dir); + + if (angle > spot_cutoff) { + float scos = max(angle, spot_cutoff); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); + + spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); + + light_att = vec3(spot_attenuation); + } else { + light_att = vec3(0.0); + } + } else { + light_att = vec3(0.0); + } + + L = normalize(light_rel_vec); + +#endif + +#ifdef LIGHT_MODE_DIRECTIONAL + vec3 light_vec = -light_direction; + light_att = vec3(1.0); //no base attenuation + L = normalize(light_vec); +#endif + + diffuse_interp = vec3(0.0); + specular_interp = vec3(0.0); + light_compute(normal_interp, L, -normalize(vertex_interp), light_color.rgb, light_att, roughness); + +#endif + +//shadows (for both vertex and fragment) +#if defined(USE_SHADOW) && defined(USE_LIGHTING) + + vec4 vi4 = vec4(vertex_interp, 1.0); + shadow_coord = light_shadow_matrix * vi4; + +#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4) + shadow_coord2 = light_shadow_matrix2 * vi4; +#endif + +#if defined(LIGHT_USE_PSSM4) + shadow_coord3 = light_shadow_matrix3 * vi4; + shadow_coord4 = light_shadow_matrix4 * vi4; + +#endif + +#endif //use shadow and use lighting + +#ifdef USE_VERTEX_LIGHTING + +#ifdef USE_REFLECTION_PROBE1 + { + vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp)); + vec3 local_pos = (refprobe1_local_matrix * vec4(vertex_interp, 1.0)).xyz; + vec3 inner_pos = abs(local_pos / refprobe1_box_extents); + float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + + { + vec3 local_ref_vec = (refprobe1_local_matrix * vec4(ref_normal, 0.0)).xyz; + refprobe1_reflection_normal_blend.xyz = local_ref_vec; + refprobe1_reflection_normal_blend.a = blend; + } +#ifndef USE_LIGHTMAP + + refprobe1_ambient_normal = (refprobe1_local_matrix * vec4(normal_interp, 0.0)).xyz; +#endif + } + +#endif //USE_REFLECTION_PROBE1 + +#ifdef USE_REFLECTION_PROBE2 + { + vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp)); + vec3 local_pos = (refprobe2_local_matrix * vec4(vertex_interp, 1.0)).xyz; + vec3 inner_pos = abs(local_pos / refprobe2_box_extents); + float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + + { + vec3 local_ref_vec = (refprobe2_local_matrix * vec4(ref_normal, 0.0)).xyz; + refprobe2_reflection_normal_blend.xyz = local_ref_vec; + refprobe2_reflection_normal_blend.a = blend; + } +#ifndef USE_LIGHTMAP + + refprobe2_ambient_normal = (refprobe2_local_matrix * vec4(normal_interp, 0.0)).xyz; +#endif + } + +#endif //USE_REFLECTION_PROBE2 + +#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + + float fog_amount = 0.0; + +#ifdef LIGHT_MODE_DIRECTIONAL + + vec3 fog_color = mix(fog_color_base.rgb, fog_sun_color_amount.rgb, fog_sun_color_amount.a * pow(max(dot(normalize(vertex_interp), light_direction), 0.0), 8.0)); +#else + vec3 fog_color = fog_color_base.rgb; +#endif + +#ifdef FOG_DEPTH_ENABLED + + { + float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex)); + + fog_amount = pow(fog_z, fog_depth_curve) * fog_color_base.a; + } +#endif + +#ifdef FOG_HEIGHT_ENABLED + { + float y = (inv_view_matrix * vec4(vertex_interp, 1.0)).y; + fog_amount = max(fog_amount, pow(smoothstep(fog_height_min, fog_height_max, y), fog_height_curve)); + } +#endif + fog_interp = vec4(fog_color, fog_amount); + +#endif //fog + +#endif //use vertex lighting + +#if defined(OVERRIDE_POSITION) + gl_Position = position; +#else + gl_Position = projection_matrix * vec4(vertex_interp, 1.0); +#endif + +#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS) + position_interp = gl_Position; +#endif +} + +/* clang-format off */ +[fragment] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +#if defined(USE_HIGHP_PRECISION) +precision highp float; +precision highp int; +#else +precision mediump float; +precision mediump int; +#endif +#endif + +#define M_PI 3.14159265359 +#define SHADER_IS_SRGB true + +// +// uniforms +// + +uniform highp mat4 inv_view_matrix; +/* clang-format on */ +uniform highp mat4 view_matrix; +uniform highp mat4 projection_matrix; +uniform highp mat4 projection_inverse_matrix; + +uniform highp mat4 world_transform; + +uniform highp float time; + +uniform highp vec2 viewport_size; + +#if defined(SCREEN_UV_USED) +uniform vec2 screen_pixel_size; +#endif + +#if defined(SCREEN_TEXTURE_USED) +uniform highp sampler2D screen_texture; //texunit:-4 +#endif +#if defined(DEPTH_TEXTURE_USED) +uniform highp sampler2D depth_texture; //texunit:-4 +#endif + +#ifdef USE_REFLECTION_PROBE1 + +#ifdef USE_VERTEX_LIGHTING + +in mediump vec4 refprobe1_reflection_normal_blend; +#ifndef USE_LIGHTMAP +in mediump vec3 refprobe1_ambient_normal; +#endif + +#else + +uniform bool refprobe1_use_box_project; +uniform highp vec3 refprobe1_box_extents; +uniform vec3 refprobe1_box_offset; +uniform highp mat4 refprobe1_local_matrix; + +#endif //use vertex lighting + +uniform bool refprobe1_exterior; + +uniform highp samplerCube reflection_probe1; //texunit:-5 + +uniform float refprobe1_intensity; +uniform vec4 refprobe1_ambient; + +#endif //USE_REFLECTION_PROBE1 + +#ifdef USE_REFLECTION_PROBE2 + +#ifdef USE_VERTEX_LIGHTING + +in mediump vec4 refprobe2_reflection_normal_blend; +#ifndef USE_LIGHTMAP +in mediump vec3 refprobe2_ambient_normal; +#endif + +#else + +uniform bool refprobe2_use_box_project; +uniform highp vec3 refprobe2_box_extents; +uniform vec3 refprobe2_box_offset; +uniform highp mat4 refprobe2_local_matrix; + +#endif //use vertex lighting + +uniform bool refprobe2_exterior; + +uniform highp samplerCube reflection_probe2; //texunit:-6 + +uniform float refprobe2_intensity; +uniform vec4 refprobe2_ambient; + +#endif //USE_REFLECTION_PROBE2 + +#define RADIANCE_MAX_LOD 6.0 + +#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + +void reflection_process(samplerCube reflection_map, +#ifdef USE_VERTEX_LIGHTING + vec3 ref_normal, +#ifndef USE_LIGHTMAP + vec3 amb_normal, +#endif + float ref_blend, + +#else //no vertex lighting + vec3 normal, vec3 vertex, + mat4 local_matrix, + bool use_box_project, vec3 box_extents, vec3 box_offset, +#endif //vertex lighting + bool exterior, float intensity, vec4 ref_ambient, float roughness, vec3 ambient, vec3 skybox, inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) { + vec4 reflection; + +#ifdef USE_VERTEX_LIGHTING + + reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb; + + float blend = ref_blend; //crappier blend formula for vertex + blend *= blend; + blend = max(0.0, 1.0 - blend); + +#else //fragment lighting + + vec3 local_pos = (local_matrix * vec4(vertex, 1.0)).xyz; + + if (any(greaterThan(abs(local_pos), box_extents))) { //out of the reflection box + return; + } + + vec3 inner_pos = abs(local_pos / box_extents); + float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + blend = mix(length(inner_pos), blend, blend); + blend *= blend; + blend = max(0.0, 1.0 - blend); + + //reflect and make local + vec3 ref_normal = normalize(reflect(vertex, normal)); + ref_normal = (local_matrix * vec4(ref_normal, 0.0)).xyz; + + if (use_box_project) { //box project + + vec3 nrdir = normalize(ref_normal); + vec3 rbmax = (box_extents - local_pos) / nrdir; + vec3 rbmin = (-box_extents - local_pos) / nrdir; + + vec3 rbminmax = mix(rbmin, rbmax, vec3(greaterThan(nrdir, vec3(0.0, 0.0, 0.0)))); + + float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z); + vec3 posonbox = local_pos + nrdir * fa; + ref_normal = posonbox - box_offset.xyz; + } + + reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb; +#endif + + if (exterior) { + reflection.rgb = mix(skybox, reflection.rgb, blend); + } + reflection.rgb *= intensity; + reflection.a = blend; + reflection.rgb *= blend; + + reflection_accum += reflection; + +#ifndef USE_LIGHTMAP + + vec4 ambient_out; +#ifndef USE_VERTEX_LIGHTING + + vec3 amb_normal = (local_matrix * vec4(normal, 0.0)).xyz; +#endif + + ambient_out.rgb = textureCubeLod(reflection_map, amb_normal, RADIANCE_MAX_LOD).rgb; + ambient_out.rgb = mix(ref_ambient.rgb, ambient_out.rgb, ref_ambient.a); + if (exterior) { + ambient_out.rgb = mix(ambient, ambient_out.rgb, blend); + } + + ambient_out.a = blend; + ambient_out.rgb *= blend; + ambient_accum += ambient_out; + +#endif +} + +#endif //use refprobe 1 or 2 + +#ifdef USE_LIGHTMAP +uniform mediump sampler2D lightmap; //texunit:-4 +uniform mediump float lightmap_energy; +#endif + +#ifdef USE_LIGHTMAP_CAPTURE +uniform mediump vec4[12] lightmap_captures; +uniform bool lightmap_capture_sky; + +#endif + +#ifdef USE_RADIANCE_MAP + +uniform samplerCube radiance_map; // texunit:-2 + +uniform mat4 radiance_inverse_xform; + +#endif + +uniform vec4 bg_color; +uniform float bg_energy; + +uniform float ambient_sky_contribution; +uniform vec4 ambient_color; +uniform float ambient_energy; + +#ifdef USE_LIGHTING + +uniform highp vec4 shadow_color; + +#ifdef USE_VERTEX_LIGHTING + +//get from vertex +in highp vec3 diffuse_interp; +in highp vec3 specular_interp; + +uniform highp vec3 light_direction; //may be used by fog, so leave here + +#else +//done in fragment +// general for all lights +uniform highp vec4 light_color; + +uniform highp float light_specular; + +// directional +uniform highp vec3 light_direction; +// omni +uniform highp vec3 light_position; + +uniform highp float light_attenuation; + +// spot +uniform highp float light_spot_attenuation; +uniform highp float light_spot_range; +uniform highp float light_spot_angle; +#endif + +//this is needed outside above if because dual paraboloid wants it +uniform highp float light_range; + +#ifdef USE_SHADOW + +uniform highp vec2 shadow_pixel_size; + +#if defined(LIGHT_MODE_OMNI) || defined(LIGHT_MODE_SPOT) +uniform highp sampler2D light_shadow_atlas; //texunit:-3 +#endif + +#ifdef LIGHT_MODE_DIRECTIONAL +uniform highp sampler2D light_directional_shadow; // texunit:-3 +uniform highp vec4 light_split_offsets; +#endif + +in highp vec4 shadow_coord; + +#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4) +in highp vec4 shadow_coord2; +#endif + +#if defined(LIGHT_USE_PSSM4) + +in highp vec4 shadow_coord3; +in highp vec4 shadow_coord4; + +#endif + +uniform vec4 light_clamp; + +#endif // light shadow + +// directional shadow + +#endif + +// +// varyings +// + +#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS) +in highp vec4 position_interp; +#endif + +in highp vec3 vertex_interp; +in vec3 normal_interp; + +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +in vec3 tangent_interp; +in vec3 binormal_interp; +#endif + +#if defined(ENABLE_COLOR_INTERP) +in vec4 color_interp; +#endif + +#if defined(ENABLE_UV_INTERP) +in vec2 uv_interp; +#endif + +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) +in vec2 uv2_interp; +#endif + +in vec3 view_interp; + +layout(location = 0) out vec4 frag_color; + +vec3 F0(float metallic, float specular, vec3 albedo) { + float dielectric = 0.16 * specular * specular; + // use albedo * metallic as colored specular reflectance at 0 angle for metallic materials; + // see https://google.github.io/filament/Filament.md.html + return mix(vec3(dielectric), albedo, vec3(metallic)); +} + +/* clang-format off */ + +FRAGMENT_SHADER_GLOBALS + +/* clang-format on */ + +#ifdef RENDER_DEPTH_DUAL_PARABOLOID + +in highp float dp_clip; + +#endif + +#ifdef USE_LIGHTING + +// This returns the G_GGX function divided by 2 cos_theta_m, where in practice cos_theta_m is either N.L or N.V. +// We're dividing this factor off because the overall term we'll end up looks like +// (see, for example, the first unnumbered equation in B. Burley, "Physically Based Shading at Disney", SIGGRAPH 2012): +// +// F(L.V) D(N.H) G(N.L) G(N.V) / (4 N.L N.V) +// +// We're basically regouping this as +// +// F(L.V) D(N.H) [G(N.L)/(2 N.L)] [G(N.V) / (2 N.V)] +// +// and thus, this function implements the [G(N.m)/(2 N.m)] part with m = L or V. +// +// The contents of the D and G (G1) functions (GGX) are taken from +// E. Heitz, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs", J. Comp. Graph. Tech. 3 (2) (2014). +// Eqns 71-72 and 85-86 (see also Eqns 43 and 80). + +/* +float G_GGX_2cos(float cos_theta_m, float alpha) { + // Schlick's approximation + // C. Schlick, "An Inexpensive BRDF Model for Physically-based Rendering", Computer Graphics Forum. 13 (3): 233 (1994) + // Eq. (19), although see Heitz (2014) the about the problems with his derivation. + // It nevertheless approximates GGX well with k = alpha/2. + float k = 0.5 * alpha; + return 0.5 / (cos_theta_m * (1.0 - k) + k); + + // float cos2 = cos_theta_m * cos_theta_m; + // float sin2 = (1.0 - cos2); + // return 1.0 / (cos_theta_m + sqrt(cos2 + alpha * alpha * sin2)); +} +*/ + +// This approximates G_GGX_2cos(cos_theta_l, alpha) * G_GGX_2cos(cos_theta_v, alpha) +// See Filament docs, Specular G section. +float V_GGX(float cos_theta_l, float cos_theta_v, float alpha) { + return 0.5 / mix(2.0 * cos_theta_l * cos_theta_v, cos_theta_l + cos_theta_v, alpha); +} + +float D_GGX(float cos_theta_m, float alpha) { + float alpha2 = alpha * alpha; + float d = 1.0 + (alpha2 - 1.0) * cos_theta_m * cos_theta_m; + return alpha2 / (M_PI * d * d); +} + +/* +float G_GGX_anisotropic_2cos(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) { + float cos2 = cos_theta_m * cos_theta_m; + float sin2 = (1.0 - cos2); + float s_x = alpha_x * cos_phi; + float s_y = alpha_y * sin_phi; + return 1.0 / max(cos_theta_m + sqrt(cos2 + (s_x * s_x + s_y * s_y) * sin2), 0.001); +} +*/ + +// This approximates G_GGX_anisotropic_2cos(cos_theta_l, ...) * G_GGX_anisotropic_2cos(cos_theta_v, ...) +// See Filament docs, Anisotropic specular BRDF section. +float V_GGX_anisotropic(float alpha_x, float alpha_y, float TdotV, float TdotL, float BdotV, float BdotL, float NdotV, float NdotL) { + float Lambda_V = NdotL * length(vec3(alpha_x * TdotV, alpha_y * BdotV, NdotV)); + float Lambda_L = NdotV * length(vec3(alpha_x * TdotL, alpha_y * BdotL, NdotL)); + return 0.5 / (Lambda_V + Lambda_L); +} + +float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi, float NdotH) { + float alpha2 = alpha_x * alpha_y; + highp vec3 v = vec3(alpha_y * cos_phi, alpha_x * sin_phi, alpha2 * NdotH); + highp float v2 = dot(v, v); + float w2 = alpha2 / v2; + float D = alpha2 * w2 * w2 * (1.0 / M_PI); + return D; + + /* float cos2 = cos_theta_m * cos_theta_m; + float sin2 = (1.0 - cos2); + float r_x = cos_phi / alpha_x; + float r_y = sin_phi / alpha_y; + float d = cos2 + sin2 * (r_x * r_x + r_y * r_y); + return 1.0 / max(M_PI * alpha_x * alpha_y * d * d, 0.001); */ +} + +float SchlickFresnel(float u) { + float m = 1.0 - u; + float m2 = m * m; + return m2 * m2 * m; // pow(m,5) +} + +float GTR1(float NdotH, float a) { + if (a >= 1.0) + return 1.0 / M_PI; + float a2 = a * a; + float t = 1.0 + (a2 - 1.0) * NdotH * NdotH; + return (a2 - 1.0) / (M_PI * log(a2) * t); +} + +void light_compute( + vec3 N, + vec3 L, + vec3 V, + vec3 B, + vec3 T, + vec3 light_color, + vec3 attenuation, + vec3 diffuse_color, + vec3 transmission, + float specular_blob_intensity, + float roughness, + float metallic, + float specular, + float rim, + float rim_tint, + float clearcoat, + float clearcoat_roughness, + float anisotropy, + inout vec3 diffuse_light, + inout vec3 specular_light, + inout float alpha) { +//this makes lights behave closer to linear, but then addition of lights looks bad +//better left disabled + +//#define SRGB_APPROX(m_var) m_var = pow(m_var,0.4545454545); +/* +#define SRGB_APPROX(m_var) {\ + float S1 = sqrt(m_var);\ + float S2 = sqrt(S1);\ + float S3 = sqrt(S2);\ + m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\ + } +*/ +#define SRGB_APPROX(m_var) + +#if defined(USE_LIGHT_SHADER_CODE) + // light is written by the light shader + + vec3 normal = N; + vec3 albedo = diffuse_color; + vec3 light = L; + vec3 view = V; + + /* clang-format off */ + +LIGHT_SHADER_CODE + + /* clang-format on */ + +#else + float NdotL = dot(N, L); + float cNdotL = max(NdotL, 0.0); // clamped NdotL + float NdotV = dot(N, V); + float cNdotV = max(abs(NdotV), 1e-6); + +#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT) + vec3 H = normalize(V + L); +#endif + +#if defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT) + float cNdotH = max(dot(N, H), 0.0); +#endif + +#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT) + float cLdotH = max(dot(L, H), 0.0); +#endif + + if (metallic < 1.0) { +#if defined(DIFFUSE_OREN_NAYAR) + vec3 diffuse_brdf_NL; +#else + float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance +#endif + +#if defined(DIFFUSE_LAMBERT_WRAP) + // energy conserving lambert wrap shader + diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); + +#elif defined(DIFFUSE_OREN_NAYAR) + + { + // see http://mimosa-pudica.net/improved-oren-nayar.html + float LdotV = dot(L, V); + + float s = LdotV - NdotL * NdotV; + float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); + + float sigma2 = roughness * roughness; // TODO: this needs checking + vec3 A = 1.0 + sigma2 * (-0.5 / (sigma2 + 0.33) + 0.17 * diffuse_color / (sigma2 + 0.13)); + float B = 0.45 * sigma2 / (sigma2 + 0.09); + + diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI); + } + +#elif defined(DIFFUSE_TOON) + + diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL); + +#elif defined(DIFFUSE_BURLEY) + + { + float FD90_minus_1 = 2.0 * cLdotH * cLdotH * roughness - 0.5; + float FdV = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotV); + float FdL = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotL); + diffuse_brdf_NL = (1.0 / M_PI) * FdV * FdL * cNdotL; + /* + float energyBias = mix(roughness, 0.0, 0.5); + float energyFactor = mix(roughness, 1.0, 1.0 / 1.51); + float fd90 = energyBias + 2.0 * VoH * VoH * roughness; + float f0 = 1.0; + float lightScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotL, 5.0); + float viewScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotV, 5.0); + + diffuse_brdf_NL = lightScatter * viewScatter * energyFactor; + */ + } +#else + // lambert + diffuse_brdf_NL = cNdotL * (1.0 / M_PI); +#endif + + SRGB_APPROX(diffuse_brdf_NL) + + diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation; + +#if defined(TRANSMISSION_USED) + diffuse_light += light_color * diffuse_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * transmission * attenuation; +#endif + +#if defined(LIGHT_USE_RIM) + float rim_light = pow(max(0.0, 1.0 - cNdotV), max(0.0, (1.0 - roughness) * 16.0)); + diffuse_light += rim_light * rim * mix(vec3(1.0), diffuse_color, rim_tint) * light_color; +#endif + } + + if (roughness > 0.0) { + +#if defined(SPECULAR_SCHLICK_GGX) + vec3 specular_brdf_NL = vec3(0.0); +#else + float specular_brdf_NL = 0.0; +#endif + +#if defined(SPECULAR_BLINN) + + //normalized blinn + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess) * cNdotL; + blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); + specular_brdf_NL = blinn; + +#elif defined(SPECULAR_PHONG) + + vec3 R = normalize(-reflect(L, N)); + float cRdotV = max(0.0, dot(R, V)); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float phong = pow(cRdotV, shininess); + phong *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); + specular_brdf_NL = (phong) / max(4.0 * cNdotV * cNdotL, 0.75); + +#elif defined(SPECULAR_TOON) + + vec3 R = normalize(-reflect(L, N)); + float RdotV = dot(R, V); + float mid = 1.0 - roughness; + mid *= mid; + specular_brdf_NL = smoothstep(mid - roughness * 0.5, mid + roughness * 0.5, RdotV) * mid; + +#elif defined(SPECULAR_DISABLED) + // none.. +#elif defined(SPECULAR_SCHLICK_GGX) + // shlick+ggx as default + +#if defined(LIGHT_USE_ANISOTROPY) + float alpha_ggx = roughness * roughness; + float aspect = sqrt(1.0 - anisotropy * 0.9); + float ax = alpha_ggx / aspect; + float ay = alpha_ggx * aspect; + float XdotH = dot(T, H); + float YdotH = dot(B, H); + float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH, cNdotH); + //float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH); + float G = V_GGX_anisotropic(ax, ay, dot(T, V), dot(T, L), dot(B, V), dot(B, L), cNdotV, cNdotL); + +#else + float alpha_ggx = roughness * roughness; + float D = D_GGX(cNdotH, alpha_ggx); + //float G = G_GGX_2cos(cNdotL, alpha_ggx) * G_GGX_2cos(cNdotV, alpha_ggx); + float G = V_GGX(cNdotL, cNdotV, alpha_ggx); +#endif + // F + vec3 f0 = F0(metallic, specular, diffuse_color); + float cLdotH5 = SchlickFresnel(cLdotH); + vec3 F = mix(vec3(cLdotH5), vec3(1.0), f0); + + specular_brdf_NL = cNdotL * D * F * G; + +#endif + + SRGB_APPROX(specular_brdf_NL) + specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; + +#if defined(LIGHT_USE_CLEARCOAT) + +#if !defined(SPECULAR_SCHLICK_GGX) + float cLdotH5 = SchlickFresnel(cLdotH); +#endif + float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_roughness)); + float Fr = mix(.04, 1.0, cLdotH5); + //float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25); + float Gr = V_GGX(cNdotL, cNdotV, 0.25); + + float clearcoat_specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL; + + specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation; +#endif + } + +#ifdef USE_SHADOW_TO_OPACITY + alpha = min(alpha, clamp(1.0 - length(attenuation), 0.0, 1.0)); +#endif + +#endif //defined(USE_LIGHT_SHADER_CODE) +} + +#endif +// shadows + +#ifdef USE_SHADOW + +#ifdef USE_RGBA_SHADOWS + +#define SHADOW_DEPTH(m_val) dot(m_val, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) + +#else + +#define SHADOW_DEPTH(m_val) (m_val).r + +#endif + +#define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, SHADOW_DEPTH(texture(p_shadow, p_pos))) +#define SAMPLE_SHADOW_TEXEL_PROJ(p_shadow, p_pos) step(p_pos.z, SHADOW_DEPTH(textureProj(p_shadow, p_pos))) + +float sample_shadow(highp sampler2D shadow, highp vec4 spos) { +#ifdef SHADOW_MODE_PCF_13 + + spos.xyz /= spos.w; + vec2 pos = spos.xy; + float depth = spos.z; + + float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, -shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, -shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x * 2.0, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x * 2.0, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y * 2.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y * 2.0), depth); + return avg * (1.0 / 13.0); +#endif + +#ifdef SHADOW_MODE_PCF_5 + + spos.xyz /= spos.w; + vec2 pos = spos.xy; + float depth = spos.z; + + float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y), depth); + return avg * (1.0 / 5.0); + +#endif + +#if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13) + + return SAMPLE_SHADOW_TEXEL_PROJ(shadow, spos); +#endif +} + +#endif + +#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + +#if defined(USE_VERTEX_LIGHTING) + +in vec4 fog_interp; + +#else +uniform mediump vec4 fog_color_base; +#ifdef LIGHT_MODE_DIRECTIONAL +uniform mediump vec4 fog_sun_color_amount; +#endif + +uniform bool fog_transmit_enabled; +uniform mediump float fog_transmit_curve; + +#ifdef FOG_DEPTH_ENABLED +uniform highp float fog_depth_begin; +uniform mediump float fog_depth_curve; +uniform mediump float fog_max_distance; +#endif + +#ifdef FOG_HEIGHT_ENABLED +uniform highp float fog_height_min; +uniform highp float fog_height_max; +uniform mediump float fog_height_curve; +#endif + +#endif //vertex lit +#endif //fog + +void main() { +#ifdef RENDER_DEPTH_DUAL_PARABOLOID + + if (dp_clip > 0.0) + discard; +#endif + highp vec3 vertex = vertex_interp; + vec3 view = -normalize(vertex_interp); + vec3 albedo = vec3(1.0); + vec3 transmission = vec3(0.0); + float metallic = 0.0; + float specular = 0.5; + vec3 emission = vec3(0.0); + float roughness = 1.0; + float rim = 0.0; + float rim_tint = 0.0; + float clearcoat = 0.0; + float clearcoat_roughness = 0.0; + float anisotropy = 0.0; + vec2 anisotropy_flow = vec2(1.0, 0.0); + float sss_strength = 0.0; //unused + // gl_FragDepth is not available in GLES2, so writing to DEPTH is not converted to gl_FragDepth by Godot compiler resulting in a + // compile error because DEPTH is not a variable. + float m_DEPTH = 0.0; + + float alpha = 1.0; + float side = 1.0; + + float specular_blob_intensity = 1.0; +#if defined(SPECULAR_TOON) + specular_blob_intensity *= specular * 2.0; +#endif + +#if defined(ENABLE_AO) + float ao = 1.0; + float ao_light_affect = 0.0; +#endif + +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + vec3 binormal = normalize(binormal_interp) * side; + vec3 tangent = normalize(tangent_interp) * side; +#else + vec3 binormal = vec3(0.0); + vec3 tangent = vec3(0.0); +#endif + vec3 normal = normalize(normal_interp) * side; + +#if defined(ENABLE_NORMALMAP) + vec3 normalmap = vec3(0.5); +#endif + float normaldepth = 1.0; + +#if defined(ALPHA_SCISSOR_USED) + float alpha_scissor = 0.5; +#endif + +#if defined(SCREEN_UV_USED) + vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; +#endif + + { + /* clang-format off */ + +FRAGMENT_SHADER_CODE + + /* clang-format on */ + } + +#if defined(ENABLE_NORMALMAP) + normalmap.xy = normalmap.xy * 2.0 - 1.0; + normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy))); + + normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side; + //normal = normalmap; +#endif + + normal = normalize(normal); + + vec3 N = normal; + + vec3 specular_light = vec3(0.0, 0.0, 0.0); + vec3 diffuse_light = vec3(0.0, 0.0, 0.0); + vec3 ambient_light = vec3(0.0, 0.0, 0.0); + + vec3 eye_position = view; + +#if !defined(USE_SHADOW_TO_OPACITY) + +#if defined(ALPHA_SCISSOR_USED) + if (alpha < alpha_scissor) { + discard; + } +#endif // ALPHA_SCISSOR_USED + +#ifdef USE_DEPTH_PREPASS + if (alpha < 0.1) { + discard; + } +#endif // USE_DEPTH_PREPASS + +#endif // !USE_SHADOW_TO_OPACITY + +#ifdef BASE_PASS + + // IBL precalculations + float ndotv = clamp(dot(normal, eye_position), 0.0, 1.0); + vec3 f0 = F0(metallic, specular, albedo); + vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0); + +#ifdef AMBIENT_LIGHT_DISABLED + ambient_light = vec3(0.0, 0.0, 0.0); +#else + +#ifdef USE_RADIANCE_MAP + + vec3 ref_vec = reflect(-eye_position, N); + ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz); + + ref_vec.z *= -1.0; + + specular_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy; +#ifndef USE_LIGHTMAP + { + vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz); + vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, 4.0).xyz * bg_energy; + env_ambient *= 1.0 - F; + + ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution); + } +#endif + +#else + + ambient_light = ambient_color.rgb; + specular_light = bg_color.rgb * bg_energy; + +#endif +#endif // AMBIENT_LIGHT_DISABLED + ambient_light *= ambient_energy; + +#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + + vec4 ambient_accum = vec4(0.0); + vec4 reflection_accum = vec4(0.0); + +#ifdef USE_REFLECTION_PROBE1 + + reflection_process(reflection_probe1, +#ifdef USE_VERTEX_LIGHTING + refprobe1_reflection_normal_blend.rgb, +#ifndef USE_LIGHTMAP + refprobe1_ambient_normal, +#endif + refprobe1_reflection_normal_blend.a, +#else + normal_interp, vertex_interp, refprobe1_local_matrix, + refprobe1_use_box_project, refprobe1_box_extents, refprobe1_box_offset, +#endif + refprobe1_exterior, refprobe1_intensity, refprobe1_ambient, roughness, + ambient_light, specular_light, reflection_accum, ambient_accum); + +#endif // USE_REFLECTION_PROBE1 + +#ifdef USE_REFLECTION_PROBE2 + + reflection_process(reflection_probe2, +#ifdef USE_VERTEX_LIGHTING + refprobe2_reflection_normal_blend.rgb, +#ifndef USE_LIGHTMAP + refprobe2_ambient_normal, +#endif + refprobe2_reflection_normal_blend.a, +#else + normal_interp, vertex_interp, refprobe2_local_matrix, + refprobe2_use_box_project, refprobe2_box_extents, refprobe2_box_offset, +#endif + refprobe2_exterior, refprobe2_intensity, refprobe2_ambient, roughness, + ambient_light, specular_light, reflection_accum, ambient_accum); + +#endif // USE_REFLECTION_PROBE2 + + if (reflection_accum.a > 0.0) { + specular_light = reflection_accum.rgb / reflection_accum.a; + } + +#ifndef USE_LIGHTMAP + if (ambient_accum.a > 0.0) { + ambient_light = ambient_accum.rgb / ambient_accum.a; + } +#endif + +#endif // defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + + // environment BRDF approximation + { +#if defined(DIFFUSE_TOON) + //simplify for toon, as + specular_light *= specular * metallic * albedo * 2.0; +#else + + // scales the specular reflections, needs to be computed before lighting happens, + // but after environment and reflection probes are added + //TODO: this curve is not really designed for gammaspace, should be adjusted + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = roughness * c0 + c1; + float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y; + vec2 env = vec2(-1.04, 1.04) * a004 + r.zw; + specular_light *= env.x * F + env.y; + +#endif + } + +#ifdef USE_LIGHTMAP + //ambient light will come entirely from lightmap is lightmap is used + ambient_light = texture(lightmap, uv2_interp).rgb * lightmap_energy; +#endif + +#ifdef USE_LIGHTMAP_CAPTURE + { + vec3 cone_dirs[12]; + cone_dirs[0] = vec3(0.0, 0.0, 1.0); + cone_dirs[1] = vec3(0.866025, 0.0, 0.5); + cone_dirs[2] = vec3(0.267617, 0.823639, 0.5); + cone_dirs[3] = vec3(-0.700629, 0.509037, 0.5); + cone_dirs[4] = vec3(-0.700629, -0.509037, 0.5); + cone_dirs[5] = vec3(0.267617, -0.823639, 0.5); + cone_dirs[6] = vec3(0.0, 0.0, -1.0); + cone_dirs[7] = vec3(0.866025, 0.0, -0.5); + cone_dirs[8] = vec3(0.267617, 0.823639, -0.5); + cone_dirs[9] = vec3(-0.700629, 0.509037, -0.5); + cone_dirs[10] = vec3(-0.700629, -0.509037, -0.5); + cone_dirs[11] = vec3(0.267617, -0.823639, -0.5); + + vec3 local_normal = normalize(inv_view_matrix * vec4(normal, 0.0)).xyz; + vec4 captured = vec4(0.0); + float sum = 0.0; + for (int i = 0; i < 12; i++) { + float amount = max(0.0, dot(local_normal, cone_dirs[i])); //not correct, but creates a nice wrap around effect + captured += lightmap_captures[i] * amount; + sum += amount; + } + + captured /= sum; + + if (lightmap_capture_sky) { + ambient_light = mix(ambient_light, captured.rgb, captured.a); + } else { + ambient_light = captured.rgb; + } + } +#endif + +#endif //BASE PASS + +// +// Lighting +// +#ifdef USE_LIGHTING + +#ifndef USE_VERTEX_LIGHTING + vec3 L; +#endif + vec3 light_att = vec3(1.0); + +#ifdef LIGHT_MODE_OMNI + +#ifndef USE_VERTEX_LIGHTING + vec3 light_vec = light_position - vertex; + float light_length = length(light_vec); + + float normalized_distance = light_length / light_range; + if (normalized_distance < 1.0) { + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); + + light_att = vec3(omni_attenuation); + } else { + light_att = vec3(0.0); + } + L = normalize(light_vec); + +#endif + +#if !defined(SHADOWS_DISABLED) + +#ifdef USE_SHADOW + { + highp vec4 splane = shadow_coord; + float shadow_len = length(splane.xyz); + + splane.xyz = normalize(splane.xyz); + + vec4 clamp_rect = light_clamp; + + if (splane.z >= 0.0) { + splane.z += 1.0; + + clamp_rect.y += clamp_rect.w; + } else { + splane.z = 1.0 - splane.z; + } + + splane.xy /= splane.z; + splane.xy = splane.xy * 0.5 + 0.5; + splane.z = shadow_len / light_range; + + splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; + splane.w = 1.0; + + float shadow = sample_shadow(light_shadow_atlas, splane); + + light_att *= mix(shadow_color.rgb, vec3(1.0), shadow); + } +#endif + +#endif //SHADOWS_DISABLED + +#endif //type omni + +#ifdef LIGHT_MODE_DIRECTIONAL + +#ifndef USE_VERTEX_LIGHTING + vec3 light_vec = -light_direction; + L = normalize(light_vec); +#endif + float depth_z = -vertex.z; + +#if !defined(SHADOWS_DISABLED) + +#ifdef USE_SHADOW + +#ifdef USE_VERTEX_LIGHTING + //compute shadows in a mobile friendly way + +#ifdef LIGHT_USE_PSSM4 + //take advantage of prefetch + float shadow1 = sample_shadow(light_directional_shadow, shadow_coord); + float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2); + float shadow3 = sample_shadow(light_directional_shadow, shadow_coord3); + float shadow4 = sample_shadow(light_directional_shadow, shadow_coord4); + + if (depth_z < light_split_offsets.w) { + float pssm_fade = 0.0; + float shadow_att = 1.0; +#ifdef LIGHT_USE_PSSM_BLEND + float shadow_att2 = 1.0; + float pssm_blend = 0.0; + bool use_blend = true; +#endif + if (depth_z < light_split_offsets.y) { + if (depth_z < light_split_offsets.x) { + shadow_att = shadow1; + +#ifdef LIGHT_USE_PSSM_BLEND + shadow_att2 = shadow2; + + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#endif + } else { + shadow_att = shadow2; + +#ifdef LIGHT_USE_PSSM_BLEND + shadow_att2 = shadow3; + + pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#endif + } + } else { + if (depth_z < light_split_offsets.z) { + shadow_att = shadow3; + +#if defined(LIGHT_USE_PSSM_BLEND) + shadow_att2 = shadow4; + pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); +#endif + + } else { + shadow_att = shadow4; + pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); + +#if defined(LIGHT_USE_PSSM_BLEND) + use_blend = false; +#endif + } + } +#if defined(LIGHT_USE_PSSM_BLEND) + if (use_blend) { + shadow_att = mix(shadow_att, shadow_att2, pssm_blend); + } +#endif + light_att *= mix(shadow_color.rgb, vec3(1.0), shadow_att); + } + +#endif //LIGHT_USE_PSSM4 + +#ifdef LIGHT_USE_PSSM2 + + //take advantage of prefetch + float shadow1 = sample_shadow(light_directional_shadow, shadow_coord); + float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2); + + if (depth_z < light_split_offsets.y) { + float shadow_att = 1.0; + float pssm_fade = 0.0; + +#ifdef LIGHT_USE_PSSM_BLEND + float shadow_att2 = 1.0; + float pssm_blend = 0.0; + bool use_blend = true; +#endif + if (depth_z < light_split_offsets.x) { + float pssm_fade = 0.0; + shadow_att = shadow1; + +#ifdef LIGHT_USE_PSSM_BLEND + shadow_att2 = shadow2; + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#endif + } else { + shadow_att = shadow2; + pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#ifdef LIGHT_USE_PSSM_BLEND + use_blend = false; +#endif + } +#ifdef LIGHT_USE_PSSM_BLEND + if (use_blend) { + shadow_att = mix(shadow_att, shadow_att2, pssm_blend); + } +#endif + light_att *= mix(shadow_color.rgb, vec3(1.0), shadow_att); + } + +#endif //LIGHT_USE_PSSM2 + +#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) + + light_att *= mix(shadow_color.rgb, vec3(1.0), sample_shadow(light_directional_shadow, shadow_coord)); +#endif //orthogonal + +#else //fragment version of pssm + + { +#ifdef LIGHT_USE_PSSM4 + if (depth_z < light_split_offsets.w) { +#elif defined(LIGHT_USE_PSSM2) + if (depth_z < light_split_offsets.y) { +#else + if (depth_z < light_split_offsets.x) { +#endif //pssm2 + + highp vec4 pssm_coord; + float pssm_fade = 0.0; + +#ifdef LIGHT_USE_PSSM_BLEND + float pssm_blend; + highp vec4 pssm_coord2; + bool use_blend = true; +#endif + +#ifdef LIGHT_USE_PSSM4 + + if (depth_z < light_split_offsets.y) { + if (depth_z < light_split_offsets.x) { + pssm_coord = shadow_coord; + +#ifdef LIGHT_USE_PSSM_BLEND + pssm_coord2 = shadow_coord2; + + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#endif + } else { + pssm_coord = shadow_coord2; + +#ifdef LIGHT_USE_PSSM_BLEND + pssm_coord2 = shadow_coord3; + + pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#endif + } + } else { + if (depth_z < light_split_offsets.z) { + pssm_coord = shadow_coord3; + +#if defined(LIGHT_USE_PSSM_BLEND) + pssm_coord2 = shadow_coord4; + pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); +#endif + + } else { + pssm_coord = shadow_coord4; + pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); + +#if defined(LIGHT_USE_PSSM_BLEND) + use_blend = false; +#endif + } + } + +#endif // LIGHT_USE_PSSM4 + +#ifdef LIGHT_USE_PSSM2 + if (depth_z < light_split_offsets.x) { + pssm_coord = shadow_coord; + +#ifdef LIGHT_USE_PSSM_BLEND + pssm_coord2 = shadow_coord2; + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#endif + } else { + pssm_coord = shadow_coord2; + pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#ifdef LIGHT_USE_PSSM_BLEND + use_blend = false; +#endif + } + +#endif // LIGHT_USE_PSSM2 + +#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) + { + pssm_coord = shadow_coord; + } +#endif + + float shadow = sample_shadow(light_directional_shadow, pssm_coord); + +#ifdef LIGHT_USE_PSSM_BLEND + if (use_blend) { + shadow = mix(shadow, sample_shadow(light_directional_shadow, pssm_coord2), pssm_blend); + } +#endif + + light_att *= mix(shadow_color.rgb, vec3(1.0), shadow); + } + } +#endif //use vertex lighting + +#endif //use shadow + +#endif // SHADOWS_DISABLED + +#endif + +#ifdef LIGHT_MODE_SPOT + + light_att = vec3(1.0); + +#ifndef USE_VERTEX_LIGHTING + + vec3 light_rel_vec = light_position - vertex; + float light_length = length(light_rel_vec); + float normalized_distance = light_length / light_range; + + if (normalized_distance < 1.0) { + float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); + vec3 spot_dir = light_direction; + + float spot_cutoff = light_spot_angle; + float angle = dot(-normalize(light_rel_vec), spot_dir); + + if (angle > spot_cutoff) { + float scos = max(angle, spot_cutoff); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); + spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); + + light_att = vec3(spot_attenuation); + } else { + light_att = vec3(0.0); + } + } else { + light_att = vec3(0.0); + } + + L = normalize(light_rel_vec); + +#endif + +#if !defined(SHADOWS_DISABLED) + +#ifdef USE_SHADOW + { + highp vec4 splane = shadow_coord; + + float shadow = sample_shadow(light_shadow_atlas, splane); + light_att *= mix(shadow_color.rgb, vec3(1.0), shadow); + } +#endif + +#endif // SHADOWS_DISABLED + +#endif // LIGHT_MODE_SPOT + +#ifdef USE_VERTEX_LIGHTING + //vertex lighting + + specular_light += specular_interp * specular_blob_intensity * light_att; + diffuse_light += diffuse_interp * albedo * light_att; + +#else + //fragment lighting + light_compute( + normal, + L, + eye_position, + binormal, + tangent, + light_color.xyz, + light_att, + albedo, + transmission, + specular_blob_intensity * light_specular, + roughness, + metallic, + specular, + rim, + rim_tint, + clearcoat, + clearcoat_roughness, + anisotropy, + diffuse_light, + specular_light, + alpha); + +#endif //vertex lighting + +#endif //USE_LIGHTING + //compute and merge + +#ifdef USE_SHADOW_TO_OPACITY + + alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); + +#if defined(ALPHA_SCISSOR_USED) + if (alpha < alpha_scissor) { + discard; + } +#endif // ALPHA_SCISSOR_USED + +#ifdef USE_DEPTH_PREPASS + if (alpha < 0.1) { + discard; + } +#endif // USE_DEPTH_PREPASS + +#endif // !USE_SHADOW_TO_OPACITY + +#ifndef RENDER_DEPTH + +#ifdef SHADELESS + + frag_color = vec4(albedo, alpha); +#else + + ambient_light *= albedo; + +#if defined(ENABLE_AO) + ambient_light *= ao; + ao_light_affect = mix(1.0, ao, ao_light_affect); + specular_light *= ao_light_affect; + diffuse_light *= ao_light_affect; +#endif + + diffuse_light *= 1.0 - metallic; + ambient_light *= 1.0 - metallic; + + frag_color = vec4(ambient_light + diffuse_light + specular_light, alpha); + + //add emission if in base pass +#ifdef BASE_PASS + frag_color.rgb += emission; +#endif + // frag_color = vec4(normal, 1.0); + +//apply fog +#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + +#if defined(USE_VERTEX_LIGHTING) + +#if defined(BASE_PASS) + frag_color.rgb = mix(frag_color.rgb, fog_interp.rgb, fog_interp.a); +#else + frag_color.rgb *= (1.0 - fog_interp.a); +#endif // BASE_PASS + +#else //pixel based fog + float fog_amount = 0.0; + +#ifdef LIGHT_MODE_DIRECTIONAL + + vec3 fog_color = mix(fog_color_base.rgb, fog_sun_color_amount.rgb, fog_sun_color_amount.a * pow(max(dot(eye_position, light_direction), 0.0), 8.0)); +#else + vec3 fog_color = fog_color_base.rgb; +#endif + +#ifdef FOG_DEPTH_ENABLED + + { + float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex)); + + fog_amount = pow(fog_z, fog_depth_curve) * fog_color_base.a; + + if (fog_transmit_enabled) { + vec3 total_light = frag_color.rgb; + float transmit = pow(fog_z, fog_transmit_curve); + fog_color = mix(max(total_light, fog_color), fog_color, transmit); + } + } +#endif + +#ifdef FOG_HEIGHT_ENABLED + { + float y = (inv_view_matrix * vec4(vertex, 1.0)).y; + fog_amount = max(fog_amount, pow(smoothstep(fog_height_min, fog_height_max, y), fog_height_curve)); + } +#endif + +#if defined(BASE_PASS) + frag_color.rgb = mix(frag_color.rgb, fog_color, fog_amount); +#else + frag_color.rgb *= (1.0 - fog_amount); +#endif // BASE_PASS + +#endif //use vertex lit + +#endif // defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + +#endif //unshaded + +#else // not RENDER_DEPTH +//depth render +#ifdef USE_RGBA_SHADOWS + + highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias + highp vec4 comp = fract(depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0)); + comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0); + frag_color = comp; + +#endif +#endif +} diff --git a/drivers/gles3/shaders/stdlib_inc.glsl b/drivers/gles3/shaders/stdlib_inc.glsl new file mode 100644 index 0000000000..2eddf9d479 --- /dev/null +++ b/drivers/gles3/shaders/stdlib_inc.glsl @@ -0,0 +1,58 @@ +//TODO: only needed by GLES_OVER_GL + +uint float2half(uint f) { + return ((f >> uint(16)) & uint(0x8000)) | + ((((f & uint(0x7f800000)) - uint(0x38000000)) >> uint(13)) & uint(0x7c00)) | + ((f >> uint(13)) & uint(0x03ff)); +} + +uint half2float(uint h) { + return ((h & uint(0x8000)) << uint(16)) | (((h & uint(0x7c00)) + uint(0x1c000)) << uint(13)) | ((h & uint(0x03ff)) << uint(13)); +} + +uint packHalf2x16(vec2 v) { + return float2half(floatBitsToUint(v.x)) | float2half(floatBitsToUint(v.y)) << uint(16); +} + +vec2 unpackHalf2x16(uint v) { + return vec2(uintBitsToFloat(half2float(v & uint(0xffff))), + uintBitsToFloat(half2float(v >> uint(16)))); +} + +uint packUnorm2x16(vec2 v) { + uvec2 uv = uvec2(round(clamp(v, vec2(0.0), vec2(1.0)) * 65535.0)); + return uv.x | uv.y << uint(16); +} + +vec2 unpackUnorm2x16(uint p) { + return vec2(float(p & uint(0xffff)), float(p >> uint(16))) * 0.000015259021; // 1.0 / 65535.0 optimization +} + +uint packSnorm2x16(vec2 v) { + uvec2 uv = uvec2(round(clamp(v, vec2(-1.0), vec2(1.0)) * 32767.0) + 32767.0); + return uv.x | uv.y << uint(16); +} + +vec2 unpackSnorm2x16(uint p) { + vec2 v = vec2(float(p & uint(0xffff)), float(p >> uint(16))); + return clamp((v - 32767.0) * vec2(0.00003051851), vec2(-1.0), vec2(1.0)); +} + +uint packUnorm4x8(vec4 v) { + uvec4 uv = uvec4(round(clamp(v, vec4(0.0), vec4(1.0)) * 255.0)); + return uv.x | uv.y << uint(8) | uv.z << uint(16) | uv.w << uint(24); +} + +vec4 unpackUnorm4x8(uint p) { + return vec4(float(p & uint(0xffff)), float((p >> uint(8)) & uint(0xffff)), float((p >> uint(16)) & uint(0xffff)), float(p >> uint(24))) * 0.00392156862; // 1.0 / 255.0 +} + +uint packSnorm4x8(vec4 v) { + uvec4 uv = uvec4(round(clamp(v, vec4(-1.0), vec4(1.0)) * 127.0) + 127.0); + return uv.x | uv.y << uint(8) | uv.z << uint(16) | uv.w << uint(24); +} + +vec4 unpackSnorm4x8(uint p) { + vec4 v = vec4(float(p & uint(0xffff)), float((p >> uint(8)) & uint(0xffff)), float((p >> uint(16)) & uint(0xffff)), float(p >> uint(24))); + return clamp((v - vec4(127.0)) * vec4(0.00787401574), vec4(-1.0), vec4(1.0)); +} diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl new file mode 100644 index 0000000000..4f962626a3 --- /dev/null +++ b/drivers/gles3/shaders/tonemap.glsl @@ -0,0 +1,313 @@ +/* clang-format off */ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +precision highp float; +precision highp int; +#endif + +layout(location = 0) in vec2 vertex_attrib; +/* clang-format on */ +layout(location = 4) in vec2 uv_in; + +out vec2 uv_interp; + +void main() { + gl_Position = vec4(vertex_attrib, 0.0, 1.0); + + uv_interp = uv_in; +} + +/* clang-format off */ +[fragment] + +#ifdef USE_GLES_OVER_GL +#define lowp +#define mediump +#define highp +#else +#if defined(USE_HIGHP_PRECISION) +precision highp float; +precision highp int; +#else +precision mediump float; +precision mediump int; +#endif +#endif + +in vec2 uv_interp; +/* clang-format on */ + +layout(location = 0) out vec4 frag_color; + +uniform highp sampler2D source; //texunit:0 + +#if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7) +#define USING_GLOW // only use glow when at least one glow level is selected + +#ifdef USE_MULTI_TEXTURE_GLOW +uniform highp sampler2D source_glow1; //texunit:2 +uniform highp sampler2D source_glow2; //texunit:3 +uniform highp sampler2D source_glow3; //texunit:4 +uniform highp sampler2D source_glow4; //texunit:5 +uniform highp sampler2D source_glow5; //texunit:6 +uniform highp sampler2D source_glow6; //texunit:7 +#ifdef USE_GLOW_LEVEL7 +uniform highp sampler2D source_glow7; //texunit:8 +#endif +#else +uniform highp sampler2D source_glow; //texunit:2 +#endif +uniform highp float glow_intensity; +#endif + +#ifdef USE_BCS +uniform vec3 bcs; +#endif + +#ifdef USE_FXAA +uniform vec2 pixel_size; +#endif + +#ifdef USE_COLOR_CORRECTION +uniform sampler2D color_correction; //texunit:1 +#endif + +#ifdef USE_GLOW_FILTER_BICUBIC +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +uniform ivec2 glow_texture_size; + +vec4 texture_bicubic(sampler2D tex, vec2 uv, int p_lod) { + float lod = float(p_lod); + vec2 tex_size = vec2(glow_texture_size >> p_lod); + vec2 texel_size = vec2(1.0) / tex_size; + + uv = uv * tex_size + vec2(0.5); + + vec2 iuv = floor(uv); + vec2 fuv = fract(uv); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + + (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); +} + +#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture_bicubic(m_tex, m_uv, m_lod) +#else //!USE_GLOW_FILTER_BICUBIC +#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod)) +#endif //USE_GLOW_FILTER_BICUBIC + +vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blending mode +#ifdef USE_GLOW_REPLACE + color = glow; +#endif + +#ifdef USE_GLOW_SCREEN + color = max((color + glow) - (color * glow), vec3(0.0)); +#endif + +#ifdef USE_GLOW_SOFTLIGHT + glow = glow * vec3(0.5) + vec3(0.5); + + color.r = (glow.r <= 0.5) ? (color.r - (1.0 - 2.0 * glow.r) * color.r * (1.0 - color.r)) : (((glow.r > 0.5) && (color.r <= 0.25)) ? (color.r + (2.0 * glow.r - 1.0) * (4.0 * color.r * (4.0 * color.r + 1.0) * (color.r - 1.0) + 7.0 * color.r)) : (color.r + (2.0 * glow.r - 1.0) * (sqrt(color.r) - color.r))); + color.g = (glow.g <= 0.5) ? (color.g - (1.0 - 2.0 * glow.g) * color.g * (1.0 - color.g)) : (((glow.g > 0.5) && (color.g <= 0.25)) ? (color.g + (2.0 * glow.g - 1.0) * (4.0 * color.g * (4.0 * color.g + 1.0) * (color.g - 1.0) + 7.0 * color.g)) : (color.g + (2.0 * glow.g - 1.0) * (sqrt(color.g) - color.g))); + color.b = (glow.b <= 0.5) ? (color.b - (1.0 - 2.0 * glow.b) * color.b * (1.0 - color.b)) : (((glow.b > 0.5) && (color.b <= 0.25)) ? (color.b + (2.0 * glow.b - 1.0) * (4.0 * color.b * (4.0 * color.b + 1.0) * (color.b - 1.0) + 7.0 * color.b)) : (color.b + (2.0 * glow.b - 1.0) * (sqrt(color.b) - color.b))); +#endif + +#if !defined(USE_GLOW_SCREEN) && !defined(USE_GLOW_SOFTLIGHT) && !defined(USE_GLOW_REPLACE) // no other selected -> additive + color += glow; +#endif + + return color; +} + +vec3 apply_bcs(vec3 color, vec3 bcs) { + color = mix(vec3(0.0), color, bcs.x); + color = mix(vec3(0.5), color, bcs.y); + color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, bcs.z); + + return color; +} + +vec3 apply_color_correction(vec3 color, sampler2D correction_tex) { + color.r = texture(correction_tex, vec2(color.r, 0.0)).r; + color.g = texture(correction_tex, vec2(color.g, 0.0)).g; + color.b = texture(correction_tex, vec2(color.b, 0.0)).b; + + return color; +} + +vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { + const float FXAA_REDUCE_MIN = (1.0 / 128.0); + const float FXAA_REDUCE_MUL = (1.0 / 8.0); + const float FXAA_SPAN_MAX = 8.0; + + vec3 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0).xyz; + vec3 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0).xyz; + vec3 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0).xyz; + vec3 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0).xyz; + vec3 rgbM = color; + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot(rgbM, luma); + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * + (0.25 * FXAA_REDUCE_MUL), + FXAA_REDUCE_MIN); + + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), + dir * rcpDirMin)) * + pixel_size; + + vec3 rgbA = 0.5 * (textureLod(source, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz); + vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source, uv_interp + dir * 0.5, 0.0).xyz); + + float lumaB = dot(rgbB, luma); + if ((lumaB < lumaMin) || (lumaB > lumaMax)) { + return rgbA; + } else { + return rgbB; + } +} + +void main() { + vec3 color = textureLod(source, uv_interp, 0.0).rgb; + +#ifdef USE_FXAA + color = apply_fxaa(color, uv_interp, pixel_size); +#endif + + // Glow + +#ifdef USING_GLOW + vec3 glow = vec3(0.0); +#ifdef USE_MULTI_TEXTURE_GLOW +#ifdef USE_GLOW_LEVEL1 + glow += GLOW_TEXTURE_SAMPLE(source_glow1, uv_interp, 0).rgb; +#ifdef USE_GLOW_LEVEL2 + glow += GLOW_TEXTURE_SAMPLE(source_glow2, uv_interp, 0).rgb; +#ifdef USE_GLOW_LEVEL3 + glow += GLOW_TEXTURE_SAMPLE(source_glow3, uv_interp, 0).rgb; +#ifdef USE_GLOW_LEVEL4 + glow += GLOW_TEXTURE_SAMPLE(source_glow4, uv_interp, 0).rgb; +#ifdef USE_GLOW_LEVEL5 + glow += GLOW_TEXTURE_SAMPLE(source_glow5, uv_interp, 0).rgb; +#ifdef USE_GLOW_LEVEL6 + glow += GLOW_TEXTURE_SAMPLE(source_glow6, uv_interp, 0).rgb; +#ifdef USE_GLOW_LEVEL7 + glow += GLOW_TEXTURE_SAMPLE(source_glow7, uv_interp, 0).rgb; +#endif +#endif +#endif +#endif +#endif +#endif +#endif + +#else + +#ifdef USE_GLOW_LEVEL1 + glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 1).rgb; +#endif + +#ifdef USE_GLOW_LEVEL2 + glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 2).rgb; +#endif + +#ifdef USE_GLOW_LEVEL3 + glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 3).rgb; +#endif + +#ifdef USE_GLOW_LEVEL4 + glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 4).rgb; +#endif + +#ifdef USE_GLOW_LEVEL5 + glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 5).rgb; +#endif + +#ifdef USE_GLOW_LEVEL6 + glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 6).rgb; +#endif + +#ifdef USE_GLOW_LEVEL7 + glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 7).rgb; +#endif +#endif //USE_MULTI_TEXTURE_GLOW + + glow *= glow_intensity; + color = apply_glow(color, glow); +#endif + + // Additional effects + +#ifdef USE_BCS + color = apply_bcs(color, bcs); +#endif + +#ifdef USE_COLOR_CORRECTION + color = apply_color_correction(color, color_correction); +#endif + + frag_color = vec4(color, 1.0); +} diff --git a/drivers/dummy/SCsub b/drivers/gles3/storage/SCsub index 91e1140b75..91e1140b75 100644 --- a/drivers/dummy/SCsub +++ b/drivers/gles3/storage/SCsub diff --git a/drivers/gles3/storage/canvas_texture_storage.cpp b/drivers/gles3/storage/canvas_texture_storage.cpp new file mode 100644 index 0000000000..fe12700c21 --- /dev/null +++ b/drivers/gles3/storage/canvas_texture_storage.cpp @@ -0,0 +1,96 @@ +/*************************************************************************/ +/* canvas_texture_storage.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "canvas_texture_storage.h" + +using namespace GLES3; + +CanvasTextureStorage *CanvasTextureStorage::singleton = nullptr; + +CanvasTextureStorage *CanvasTextureStorage::get_singleton() { + return singleton; +} + +CanvasTextureStorage::CanvasTextureStorage() { + singleton = this; +} + +CanvasTextureStorage::~CanvasTextureStorage() { + singleton = nullptr; +} + +RID CanvasTextureStorage::canvas_texture_allocate() { + return canvas_texture_owner.allocate_rid(); +} + +void CanvasTextureStorage::canvas_texture_initialize(RID p_rid) { + canvas_texture_owner.initialize_rid(p_rid); +} + +void CanvasTextureStorage::canvas_texture_free(RID p_rid) { + canvas_texture_owner.free(p_rid); +} + +void CanvasTextureStorage::canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + switch (p_channel) { + case RS::CANVAS_TEXTURE_CHANNEL_DIFFUSE: { + ct->diffuse = p_texture; + } break; + case RS::CANVAS_TEXTURE_CHANNEL_NORMAL: { + ct->normal_map = p_texture; + } break; + case RS::CANVAS_TEXTURE_CHANNEL_SPECULAR: { + ct->specular = p_texture; + } break; + } +} + +void CanvasTextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ct->specular_color.r = p_specular_color.r; + ct->specular_color.g = p_specular_color.g; + ct->specular_color.b = p_specular_color.b; + ct->specular_color.a = p_shininess; +} + +void CanvasTextureStorage::canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ct->texture_filter = p_filter; +} + +void CanvasTextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ct->texture_repeat = p_repeat; +} + +#endif // !GLES3_ENABLED diff --git a/drivers/gles3/storage/canvas_texture_storage.h b/drivers/gles3/storage/canvas_texture_storage.h new file mode 100644 index 0000000000..5930e927fe --- /dev/null +++ b/drivers/gles3/storage/canvas_texture_storage.h @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* canvas_texture_storage.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 CANVAS_TEXTURE_STORAGE_GLES3_H +#define CANVAS_TEXTURE_STORAGE_GLES3_H + +#ifdef GLES3_ENABLED + +#include "core/templates/rid_owner.h" +#include "servers/rendering/storage/canvas_texture_storage.h" + +namespace GLES3 { + +struct CanvasTexture { + RID diffuse; + RID normal_map; + RID specular; + Color specular_color = Color(1, 1, 1, 1); + float shininess = 1.0; + + RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT; + RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT; + + Size2i size_cache = Size2i(1, 1); + bool use_normal_cache = false; + bool use_specular_cache = false; + bool cleared_cache = true; +}; + +class CanvasTextureStorage : public RendererCanvasTextureStorage { +private: + static CanvasTextureStorage *singleton; + + RID_Owner<CanvasTexture, true> canvas_texture_owner; + +public: + static CanvasTextureStorage *get_singleton(); + + CanvasTextureStorage(); + virtual ~CanvasTextureStorage(); + + CanvasTexture *get_canvas_texture(RID p_rid) { return canvas_texture_owner.get_or_null(p_rid); }; + bool owns_canvas_texture(RID p_rid) { return canvas_texture_owner.owns(p_rid); }; + + virtual RID canvas_texture_allocate() override; + virtual void canvas_texture_initialize(RID p_rid) override; + virtual void canvas_texture_free(RID p_rid) override; + + virtual void canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) override; + virtual void canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_base_color, float p_shininess) override; + + virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override; + virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override; +}; + +} // namespace GLES3 + +#endif // !GLES3_ENABLED + +#endif // !CANVAS_TEXTURE_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp new file mode 100644 index 0000000000..1f66401427 --- /dev/null +++ b/drivers/gles3/storage/config.cpp @@ -0,0 +1,156 @@ +/*************************************************************************/ +/* config.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef GLES3_ENABLED + +#include "config.h" +#include "core/templates/vector.h" + +using namespace GLES3; + +Config *Config::singleton = nullptr; + +Config::Config() { + singleton = this; + should_orphan = true; +} + +Config::~Config() { + singleton = nullptr; +} + +void Config::initialize() { + { + const GLubyte *extension_string = glGetString(GL_EXTENSIONS); + + Vector<String> exts = String((const char *)extension_string).split(" "); + + for (int i = 0; i < exts.size(); i++) { + extensions.insert(exts[i]); + } + } + + keep_original_textures = true; // false + shrink_textures_x2 = false; + depth_internalformat = GL_DEPTH_COMPONENT; + depth_type = GL_UNSIGNED_INT; + +#ifdef GLES_OVER_GL + float_texture_supported = true; + s3tc_supported = true; + etc_supported = false; + support_npot_repeat_mipmap = true; + depth_buffer_internalformat = GL_DEPTH_COMPONENT24; +#else + float_texture_supported = extensions.has("GL_ARB_texture_float") || extensions.has("GL_OES_texture_float"); + s3tc_supported = extensions.has("GL_EXT_texture_compression_s3tc") || extensions.has("WEBGL_compressed_texture_s3tc"); + etc_supported = extensions.has("GL_OES_compressed_ETC1_RGB8_texture") || extensions.has("WEBGL_compressed_texture_etc1"); + support_npot_repeat_mipmap = extensions.has("GL_OES_texture_npot"); + +#ifdef JAVASCRIPT_ENABLED + // RenderBuffer internal format must be 16 bits in WebGL, + // but depth_texture should default to 32 always + // if the implementation doesn't support 32, it should just quietly use 16 instead + // https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/ + depth_buffer_internalformat = GL_DEPTH_COMPONENT16; + depth_type = GL_UNSIGNED_INT; +#else + // on mobile check for 24 bit depth support for RenderBufferStorage + if (extensions.has("GL_OES_depth24")) { + depth_buffer_internalformat = _DEPTH_COMPONENT24_OES; + depth_type = GL_UNSIGNED_INT; + } else { + depth_buffer_internalformat = GL_DEPTH_COMPONENT16; + depth_type = GL_UNSIGNED_SHORT; + } +#endif +#endif + +#ifdef GLES_OVER_GL + //TODO: causes huge problems with desktop video drivers. Making false for now, needs to be true to render SCREEN_TEXTURE mipmaps + render_to_mipmap_supported = false; +#else + //check if mipmaps can be used for SCREEN_TEXTURE and Glow on Mobile and web platforms + render_to_mipmap_supported = extensions.has("GL_OES_fbo_render_mipmap") && extensions.has("GL_EXT_texture_lod"); +#endif + +#ifdef GLES_OVER_GL + use_rgba_2d_shadows = false; + support_depth_texture = true; + use_rgba_3d_shadows = false; + support_depth_cubemaps = true; +#else + use_rgba_2d_shadows = !(float_texture_supported && extensions.has("GL_EXT_texture_rg")); + support_depth_texture = extensions.has("GL_OES_depth_texture") || extensions.has("WEBGL_depth_texture"); + use_rgba_3d_shadows = !support_depth_texture; + support_depth_cubemaps = extensions.has("GL_OES_depth_texture_cube_map"); +#endif + +#ifdef GLES_OVER_GL + support_32_bits_indices = true; +#else + support_32_bits_indices = extensions.has("GL_OES_element_index_uint"); +#endif + +#ifdef GLES_OVER_GL + support_write_depth = true; +#elif defined(JAVASCRIPT_ENABLED) + support_write_depth = false; +#else + support_write_depth = extensions.has("GL_EXT_frag_depth"); +#endif + + support_half_float_vertices = true; +//every platform should support this except web, iOS has issues with their support, so add option to disable +#ifdef JAVASCRIPT_ENABLED + support_half_float_vertices = false; +#endif + bool disable_half_float = false; //GLOBAL_GET("rendering/opengl/compatibility/disable_half_float"); + if (disable_half_float) { + support_half_float_vertices = false; + } + + etc_supported = extensions.has("GL_OES_compressed_ETC1_RGB8_texture"); + latc_supported = extensions.has("GL_EXT_texture_compression_latc"); + bptc_supported = extensions.has("GL_ARB_texture_compression_bptc"); + rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc"); + bptc_supported = extensions.has("GL_ARB_texture_compression_bptc") || extensions.has("EXT_texture_compression_bptc"); + srgb_decode_supported = extensions.has("GL_EXT_texture_sRGB_decode"); + + glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units); + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_texture_image_units); + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); + + force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); + use_fast_texture_filter = false; //GLOBAL_GET("rendering/quality/filters/use_nearest_mipmap_filter"); + // should_orphan = GLOBAL_GET("rendering/options/api_usage_legacy/orphan_buffers"); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h new file mode 100644 index 0000000000..25bd3fd9a1 --- /dev/null +++ b/drivers/gles3/storage/config.h @@ -0,0 +1,113 @@ +/*************************************************************************/ +/* config.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CONFIG_GLES3_H +#define CONFIG_GLES3_H + +#ifdef GLES3_ENABLED + +#include "core/string/ustring.h" +#include "core/templates/set.h" + +// This must come first to avoid windows.h mess +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif + +namespace GLES3 { + +class Config { +private: + static Config *singleton; + +public: + bool shrink_textures_x2; + bool use_fast_texture_filter; + bool use_skeleton_software; + + int max_vertex_texture_image_units; + int max_texture_image_units; + int max_texture_size; + + // TODO implement wireframe in OpenGL + // bool generate_wireframes; + + Set<String> extensions; + + bool float_texture_supported; + bool s3tc_supported; + bool latc_supported; + bool rgtc_supported; + bool bptc_supported; + bool etc_supported; + bool etc2_supported; + bool srgb_decode_supported; + + bool keep_original_textures; + + bool force_vertex_shading; + + bool use_rgba_2d_shadows; + bool use_rgba_3d_shadows; + + bool support_32_bits_indices; + bool support_write_depth; + bool support_half_float_vertices; + bool support_npot_repeat_mipmap; + bool support_depth_texture; + bool support_depth_cubemaps; + + bool support_shadow_cubemaps; + + bool render_to_mipmap_supported; + + GLuint depth_internalformat; + GLuint depth_type; + GLuint depth_buffer_internalformat; + + // in some cases the legacy render didn't orphan. We will mark these + // so the user can switch orphaning off for them. + bool should_orphan = true; + + static Config *get_singleton() { return singleton; }; + + Config(); + ~Config(); + void initialize(); +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // !CONFIG_GLES3_H diff --git a/drivers/gles3/storage/decal_atlas_storage.cpp b/drivers/gles3/storage/decal_atlas_storage.cpp new file mode 100644 index 0000000000..7bac34ea19 --- /dev/null +++ b/drivers/gles3/storage/decal_atlas_storage.cpp @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* decal_atlas_storage.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "decal_atlas_storage.h" + +using namespace GLES3; + +RID DecalAtlasStorage::decal_allocate() { + return RID(); +} + +void DecalAtlasStorage::decal_initialize(RID p_rid) { +} + +void DecalAtlasStorage::decal_set_extents(RID p_decal, const Vector3 &p_extents) { +} + +void DecalAtlasStorage::decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) { +} + +void DecalAtlasStorage::decal_set_emission_energy(RID p_decal, float p_energy) { +} + +void DecalAtlasStorage::decal_set_albedo_mix(RID p_decal, float p_mix) { +} + +void DecalAtlasStorage::decal_set_modulate(RID p_decal, const Color &p_modulate) { +} + +void DecalAtlasStorage::decal_set_cull_mask(RID p_decal, uint32_t p_layers) { +} + +void DecalAtlasStorage::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) { +} + +void DecalAtlasStorage::decal_set_fade(RID p_decal, float p_above, float p_below) { +} + +void DecalAtlasStorage::decal_set_normal_fade(RID p_decal, float p_fade) { +} + +AABB DecalAtlasStorage::decal_get_aabb(RID p_decal) const { + return AABB(); +} + +#endif // !GLES3_ENABLED diff --git a/drivers/gles3/storage/decal_atlas_storage.h b/drivers/gles3/storage/decal_atlas_storage.h new file mode 100644 index 0000000000..f5dc36b1fb --- /dev/null +++ b/drivers/gles3/storage/decal_atlas_storage.h @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* decal_atlas_storage.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 DECAL_ATLAS_STORAGE_GLES3_H +#define DECAL_ATLAS_STORAGE_GLES3_H + +#ifdef GLES3_ENABLED + +#include "core/templates/rid_owner.h" +#include "servers/rendering/storage/decal_atlas_storage.h" + +namespace GLES3 { + +class DecalAtlasStorage : public RendererDecalAtlasStorage { +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_set_extents(RID p_decal, const Vector3 &p_extents) override; + virtual void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) override; + virtual void decal_set_emission_energy(RID p_decal, float p_energy) override; + virtual void decal_set_albedo_mix(RID p_decal, float p_mix) override; + virtual void decal_set_modulate(RID p_decal, const Color &p_modulate) override; + virtual void decal_set_cull_mask(RID p_decal, uint32_t p_layers) override; + virtual void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) override; + virtual void decal_set_fade(RID p_decal, float p_above, float p_below) override; + virtual void decal_set_normal_fade(RID p_decal, float p_fade) override; + + virtual AABB decal_get_aabb(RID p_decal) const override; + + virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} + virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} +}; + +} // namespace GLES3 + +#endif // !GLES3_ENABLED + +#endif // !DECAL_ATLAS_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/render_target_storage.h b/drivers/gles3/storage/render_target_storage.h new file mode 100644 index 0000000000..816cc76e40 --- /dev/null +++ b/drivers/gles3/storage/render_target_storage.h @@ -0,0 +1,132 @@ +/*************************************************************************/ +/* render_target_storage.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDER_TARGET_STORAGE_GLES3_H +#define RENDER_TARGET_STORAGE_GLES3_H + +#ifdef GLES3_ENABLED + +#include "core/templates/rid_owner.h" +#include "servers/rendering/renderer_compositor.h" +#include "servers/rendering/renderer_storage.h" // included until we move stuff into storage/render_target_storage.h +// #include "servers/rendering/storage/render_target_storage.h" + +// This must come first to avoid windows.h mess +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif + +namespace GLES3 { + +// NOTE, this class currently is just a container for the the RenderTarget struct and is not yet implemented further, we'll do that next after we finish with TextureStorage + +struct RenderTarget { + RID self; + GLuint fbo = 0; + GLuint color = 0; + GLuint depth = 0; + + GLuint multisample_fbo = 0; + GLuint multisample_color = 0; + GLuint multisample_depth = 0; + bool multisample_active = false; + + struct Effect { + GLuint fbo = 0; + int width = 0; + int height = 0; + + GLuint color = 0; + }; + + Effect copy_screen_effect; + + struct MipMaps { + struct Size { + GLuint fbo = 0; + GLuint color = 0; + int width = 0; + int height = 0; + }; + + Vector<Size> sizes; + GLuint color = 0; + int levels = 0; + }; + + MipMaps mip_maps[2]; + + struct External { + GLuint fbo = 0; + GLuint color = 0; + GLuint depth = 0; + RID texture; + } external; + + int x = 0; + int y = 0; + int width = 0; + int height = 0; + + bool flags[RendererStorage::RENDER_TARGET_FLAG_MAX] = {}; + + // instead of allocating sized render targets immediately, + // defer this for faster startup + bool allocate_is_dirty = false; + bool used_in_frame = false; + RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; + + bool use_fxaa = false; + bool use_debanding = false; + + RID texture; + + bool used_dof_blur_near = false; + bool mip_maps_allocated = false; + + Color clear_color = Color(1, 1, 1, 1); + bool clear_requested = false; + + RenderTarget() { + for (int i = 0; i < RendererStorage::RENDER_TARGET_FLAG_MAX; ++i) { + flags[i] = false; + } + external.fbo = 0; + } +}; + +} // namespace GLES3 + +#endif // !GLES3_ENABLED + +#endif // !RENDER_TARGET_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp new file mode 100644 index 0000000000..d199b1032e --- /dev/null +++ b/drivers/gles3/storage/texture_storage.cpp @@ -0,0 +1,1211 @@ +/*************************************************************************/ +/* texture_storage.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "texture_storage.h" +#include "config.h" + +using namespace GLES3; + +TextureStorage *TextureStorage::singleton = nullptr; + +TextureStorage *TextureStorage::get_singleton() { + return singleton; +} + +TextureStorage::TextureStorage() { + singleton = this; +} + +TextureStorage::~TextureStorage() { + singleton = nullptr; +} + +void TextureStorage::set_main_thread_id(Thread::ID p_id) { + _main_thread_id = p_id; +} + +bool TextureStorage::_is_main_thread() { + //#if defined DEBUG_ENABLED && defined TOOLS_ENABLED + // must be called from main thread in OpenGL + bool is_main_thread = _main_thread_id == Thread::get_caller_id(); + //#endif + return is_main_thread; +} + +bool TextureStorage::can_create_resources_async() const { + return false; +} + +static const GLenum _cube_side_enum[6] = { + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, +}; + +Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, 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; + + bool need_decompress = false; + + switch (p_format) { + case Image::FORMAT_L8: { +#ifdef GLES_OVER_GL + r_gl_internal_format = GL_R8; + r_gl_format = GL_RED; + r_gl_type = GL_UNSIGNED_BYTE; +#else + r_gl_internal_format = GL_LUMINANCE; + r_gl_format = GL_LUMINANCE; + r_gl_type = GL_UNSIGNED_BYTE; +#endif + } break; + case Image::FORMAT_LA8: { +#ifdef GLES_OVER_GL + r_gl_internal_format = GL_RG8; + r_gl_format = GL_RG; + r_gl_type = GL_UNSIGNED_BYTE; +#else + r_gl_internal_format = GL_LUMINANCE_ALPHA; + r_gl_format = GL_LUMINANCE_ALPHA; + r_gl_type = GL_UNSIGNED_BYTE; +#endif + } break; + case Image::FORMAT_R8: { + 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; + //r_srgb = true; + + } break; + case Image::FORMAT_RGBA8: { + r_gl_format = GL_RGBA; + r_gl_internal_format = GL_RGBA8; + r_gl_type = GL_UNSIGNED_BYTE; + //r_srgb = true; + + } 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_RGBA5551: { + // r_gl_internal_format = GL_RGB5_A1; + // r_gl_format = GL_RGBA; + // r_gl_type = GL_UNSIGNED_SHORT_5_5_5_1; + // + //} break; + case Image::FORMAT_RF: { + r_gl_internal_format = GL_R32F; + r_gl_format = GL_RED; + r_gl_type = GL_FLOAT; + + } break; + case Image::FORMAT_RGF: { + r_gl_internal_format = GL_RG32F; + r_gl_format = GL_RG; + r_gl_type = GL_FLOAT; + + } break; + case Image::FORMAT_RGBF: { + r_gl_internal_format = GL_RGB32F; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; + + } break; + case Image::FORMAT_RGBAF: { + r_gl_internal_format = GL_RGBA32F; + r_gl_format = GL_RGBA; + r_gl_type = GL_FLOAT; + + } break; + case Image::FORMAT_RH: { + r_gl_internal_format = GL_R16F; + r_gl_format = GL_RED; + r_gl_type = GL_HALF_FLOAT; + } break; + case Image::FORMAT_RGH: { + 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; + case Image::FORMAT_DXT1: { + if (config->s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + //r_srgb = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_DXT3: { + if (config->s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + //r_srgb = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_DXT5: { + if (config->s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + //r_srgb = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_RGTC_R: { + if (config->rgtc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RED_RGTC1_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_RGTC_RG: { + if (config->rgtc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_BPTC_RGBA: { + if (config->bptc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_BPTC_UNORM; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + //r_srgb = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_BPTC_RGBF: { + if (config->bptc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_BPTC_RGBFU: { + if (config->bptc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC: { + if (config->etc_supported) { + r_gl_internal_format = _EXT_ETC1_RGB8_OES; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + + } break; + /* + case Image::FORMAT_ETC2_R11: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_R11_EAC; + r_gl_format = GL_RED; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_R11S: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_SIGNED_R11_EAC; + r_gl_format = GL_RED; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_RG11: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RG11_EAC; + r_gl_format = GL_RG; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_RG11S: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_SIGNED_RG11_EAC; + r_gl_format = GL_RG; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_RGB8: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGB8_ETC2; + r_gl_format = GL_RGB; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + //r_srgb = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_RGBA8: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA8_ETC2_EAC; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + //r_srgb = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_RGB8A1: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + //r_srgb = true; + + } else { + need_decompress = true; + } + } break; + */ + default: { + ERR_FAIL_V(Ref<Image>()); + } + } + + if (need_decompress || p_force_decompress) { + if (!image.is_null()) { + image = image->duplicate(); + image->decompress(); + ERR_FAIL_COND_V(image->is_compressed(), image); + switch (image->get_format()) { + 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; + } + } + + return image; + } + + return p_image; +} + +void TextureStorage::_texture_set_state_from_flags(Texture *p_tex) { + // Config *config = Config::get_singleton(); + + if ((p_tex->flags & TEXTURE_FLAG_MIPMAPS) && !p_tex->ignore_mipmaps) { + if (p_tex->flags & TEXTURE_FLAG_FILTER) { + // these do not exactly correspond ... + p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS); + //texture->glTexParam_MinFilter(texture->target, config->use_fast_texture_filter ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR); + } else { + p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); + //texture->glTexParam_MinFilter(texture->target, config->use_fast_texture_filter ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR); + } + } else { + if (p_tex->flags & TEXTURE_FLAG_FILTER) { + p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR); + //texture->glTexParam_MinFilter(texture->target, GL_LINEAR); + } else { + p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); + //texture->glTexParam_MinFilter(texture->target, GL_NEAREST); + } + } + + if (((p_tex->flags & TEXTURE_FLAG_REPEAT) || (p_tex->flags & TEXTURE_FLAG_MIRRORED_REPEAT)) && p_tex->target != GL_TEXTURE_CUBE_MAP) { + if (p_tex->flags & TEXTURE_FLAG_MIRRORED_REPEAT) { + p_tex->GLSetRepeat(p_tex->target, RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR); + } else { + p_tex->GLSetRepeat(p_tex->target, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + } + } else { + p_tex->GLSetRepeat(p_tex->target, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } +} + +void TextureStorage::_texture_allocate_internal(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, RenderingDevice::TextureType p_type, uint32_t p_flags) { + // GLenum format; + // GLenum internal_format; + // GLenum type; + + // bool compressed = false; + + // Config *config = Config::get_singleton(); + + if (p_flags & TEXTURE_FLAG_USED_FOR_STREAMING) { + p_flags &= ~TEXTURE_FLAG_MIPMAPS; // no mipies for video + } + + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + texture->width = p_width; + texture->height = p_height; + texture->format = p_format; + texture->flags = p_flags; + texture->stored_cube_sides = 0; + texture->type = p_type; + + switch (p_type) { + case RenderingDevice::TEXTURE_TYPE_2D: { + texture->target = GL_TEXTURE_2D; + texture->images.resize(1); + } break; + // case RenderingDevice::TEXTURE_TYPE_EXTERNAL: { + //#ifdef ANDROID_ENABLED + // texture->target = _GL_TEXTURE_EXTERNAL_OES; + //#else + // texture->target = GL_TEXTURE_2D; + //#endif + // texture->images.resize(0); + // } break; + case RenderingDevice::TEXTURE_TYPE_CUBE: { + texture->target = GL_TEXTURE_CUBE_MAP; + texture->images.resize(6); + } break; + case RenderingDevice::TEXTURE_TYPE_2D_ARRAY: + case RenderingDevice::TEXTURE_TYPE_3D: { + texture->target = GL_TEXTURE_3D; + ERR_PRINT("3D textures and Texture Arrays are not supported in OpenGL. Please switch to the Vulkan backend."); + return; + } break; + default: { + ERR_PRINT("Unknown texture type!"); + return; + } + } + +#if 0 + // if (p_type != RS::TEXTURE_TYPE_EXTERNAL) { + if (p_type == RenderingDevice::TEXTURE_TYPE_2D) { + texture->alloc_width = texture->width; + texture->alloc_height = texture->height; + texture->resize_to_po2 = false; + if (!config->support_npot_repeat_mipmap) { + int po2_width = next_power_of_2(p_width); + int po2_height = next_power_of_2(p_height); + + bool is_po2 = p_width == po2_width && p_height == po2_height; + + if (!is_po2 && (p_flags & TEXTURE_FLAG_REPEAT || p_flags & TEXTURE_FLAG_MIPMAPS)) { + if (p_flags & TEXTURE_FLAG_USED_FOR_STREAMING) { + //not supported + ERR_PRINT("Streaming texture for non power of 2 or has mipmaps on this hardware: " + texture->path + "'. Mipmaps and repeat disabled."); + texture->flags &= ~(TEXTURE_FLAG_REPEAT | TEXTURE_FLAG_MIPMAPS); + } else { + texture->alloc_height = po2_height; + texture->alloc_width = po2_width; + texture->resize_to_po2 = true; + } + } + } + + GLenum format; + GLenum internal_format; + GLenum type; + bool compressed = false; + + Image::Format real_format; + _get_gl_image_and_format(Ref<Image>(), + texture->format, + texture->flags, + real_format, + format, + internal_format, + type, + compressed, + texture->resize_to_po2); + + texture->gl_format_cache = format; + texture->gl_type_cache = type; + texture->gl_internal_format_cache = internal_format; + texture->data_size = 0; + texture->mipmaps = 1; + + texture->compressed = compressed; + } +#endif + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + // if (p_type == RS::TEXTURE_TYPE_EXTERNAL) { + // 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); + // } else if (p_flags & TEXTURE_FLAG_USED_FOR_STREAMING) { + // //prealloc if video + // glTexImage2D(texture->target, 0, internal_format, texture->alloc_width, texture->alloc_height, 0, format, type, NULL); + // } + + texture->active = true; +} + +RID TextureStorage::texture_create() { + ERR_FAIL_COND_V(!_is_main_thread(), RID()); + + Texture *texture = memnew(Texture); + ERR_FAIL_COND_V(!texture, RID()); + glGenTextures(1, &texture->tex_id); + texture->active = false; + texture->total_data_size = 0; + + return texture_owner.make_rid(texture); +} + +RID TextureStorage::texture_allocate() { + RID id = texture_create(); + ERR_FAIL_COND_V(id == RID(), id); + return id; +} + +void TextureStorage::texture_free(RID p_rid) { + Texture *t = texture_owner.get_or_null(p_rid); + + // can't free a render target texture + ERR_FAIL_COND(t->render_target); + if (t->canvas_texture) { + memdelete(t->canvas_texture); + } + + // info.texture_mem -= t->total_data_size; // TODO make this work again!! + texture_owner.free(p_rid); + memdelete(t); +} + +void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) { + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!tex); + + int w = p_image->get_width(); + int h = p_image->get_height(); + + _texture_allocate_internal(p_texture, w, h, 1, p_image->get_format(), RenderingDevice::TEXTURE_TYPE_2D, 0); + texture_set_data(p_texture, p_image); +} + +void TextureStorage::texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) { +} + +void TextureStorage::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) { +} + +void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) { + texture_set_proxy(p_texture, p_base); +} + +//RID TextureStorage::texture_2d_create(const Ref<Image> &p_image) { +// RID id = texture_create(); +// ERR_FAIL_COND_V(id == RID(), id); + +// int w = p_image->get_width(); +// int h = p_image->get_height(); + +// texture_allocate(id, w, h, 1, p_image->get_format(), RenderingDevice::TEXTURE_TYPE_2D, 0); + +// texture_set_data(id, p_image); + +// return id; +//} + +//RID TextureStorage::texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) { +// return RID(); +//} + +//void TextureStorage::texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer) { +// // only 1 layer so far +// texture_set_data(p_texture, p_image); +//} + +void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) { + // only 1 layer so far + texture_set_data(p_texture, p_image); +} + +void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { +} + +void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) { +} + +void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) { +} + +Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!tex, Ref<Image>()); + + /* +#ifdef TOOLS_ENABLED + if (tex->image_cache_2d.is_valid()) { + return tex->image_cache_2d; + } +#endif + Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0); + ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); + Ref<Image> image; + image.instance(); + image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); + ERR_FAIL_COND_V(image->empty(), Ref<Image>()); + if (tex->format != tex->validated_format) { + image->convert(tex->format); + } + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + tex->image_cache_2d = image; + } +#endif +*/ + ERR_FAIL_COND_V(!tex->images.size(), Ref<Image>()); + + return tex->images[0]; + + // return image; + + // return Ref<Image>(); +} + +void TextureStorage::texture_replace(RID p_texture, RID p_by_texture) { + Texture *tex_to = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!tex_to); + Texture *tex_from = texture_owner.get_or_null(p_by_texture); + ERR_FAIL_COND(!tex_from); + + tex_to->destroy(); + tex_to->copy_from(*tex_from); + + // copy image data and upload to GL + tex_to->images.resize(tex_from->images.size()); + + for (int n = 0; n < tex_from->images.size(); n++) { + texture_set_data(p_texture, tex_from->images[n], n); + } + + texture_free(p_by_texture); +} + +void TextureStorage::texture_set_size_override(RID p_texture, int p_width, int p_height) { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND(!texture); + ERR_FAIL_COND(texture->render_target); + + ERR_FAIL_COND(p_width <= 0 || p_width > 16384); + ERR_FAIL_COND(p_height <= 0 || p_height > 16384); + //real texture size is in alloc width and height + texture->width = p_width; + texture->height = p_height; +} + +void TextureStorage::texture_set_path(RID p_texture, const String &p_path) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->path = p_path; +} + +String TextureStorage::texture_get_path(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!texture, ""); + + return texture->path; +} + +void TextureStorage::texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_3d = p_callback; + texture->detect_3d_ud = p_userdata; +} + +void TextureStorage::texture_set_detect_srgb_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_srgb = p_callback; + texture->detect_srgb_ud = p_userdata; +} + +void TextureStorage::texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_normal = p_callback; + texture->detect_normal_ud = p_userdata; +} + +void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) { + List<RID> textures; + texture_owner.get_owned_list(&textures); + + for (List<RID>::Element *E = textures.front(); E; E = E->next()) { + Texture *t = texture_owner.get_or_null(E->get()); + if (!t) { + continue; + } + RS::TextureInfo tinfo; + tinfo.path = t->path; + tinfo.format = t->format; + tinfo.width = t->alloc_width; + tinfo.height = t->alloc_height; + tinfo.depth = 0; + tinfo.bytes = t->total_data_size; + r_info->push_back(tinfo); + } +} + +void TextureStorage::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->redraw_if_visible = p_enable; +} + +Size2 TextureStorage::texture_size_with_proxy(RID p_texture) { + const Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!texture, Size2()); + if (texture->proxy) { + return Size2(texture->proxy->width, texture->proxy->height); + } else { + return Size2(texture->width, texture->height); + } +} + +// example use in 3.2 +// VS::get_singleton()->texture_set_proxy(default_texture->proxy, texture_rid); + +// p_proxy is the source (pre-existing) texture? +// and p_texture is the one that is being made into a proxy? +//This naming is confusing. Comments!!! + +// The naming of the parameters seemed to be reversed? +// The p_proxy is the source texture +// and p_texture is actually the proxy???? + +void TextureStorage::texture_set_proxy(RID p_texture, RID p_proxy) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + if (texture->proxy) { + texture->proxy->proxy_owners.erase(texture); + texture->proxy = nullptr; + } + + if (p_proxy.is_valid()) { + Texture *proxy = texture_owner.get_or_null(p_proxy); + ERR_FAIL_COND(!proxy); + ERR_FAIL_COND(proxy == texture); + proxy->proxy_owners.insert(texture); + texture->proxy = proxy; + } +} + +void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) { + Config *config = Config::get_singleton(); + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND(!_is_main_thread()); + + ERR_FAIL_COND(!texture); + if (texture->target == GL_TEXTURE_3D) { + // Target is set to a 3D texture or array texture, exit early to avoid spamming errors + return; + } + ERR_FAIL_COND(!texture->active); + ERR_FAIL_COND(texture->render_target); + ERR_FAIL_COND(p_image.is_null()); + ERR_FAIL_COND(texture->format != p_image->get_format()); + + ERR_FAIL_COND(!p_image->get_width()); + ERR_FAIL_COND(!p_image->get_height()); + + // ERR_FAIL_COND(texture->type == RS::TEXTURE_TYPE_EXTERNAL); + + GLenum type; + GLenum format; + GLenum internal_format; + bool compressed = false; + + if (config->keep_original_textures && !(texture->flags & TEXTURE_FLAG_USED_FOR_STREAMING)) { + texture->images.write[p_layer] = p_image; + } + + // print_line("texture_set_data width " + itos (p_image->get_width()) + " height " + itos(p_image->get_height())); + + Image::Format real_format; + Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, real_format, format, internal_format, type, compressed, texture->resize_to_po2); + + if (texture->resize_to_po2) { + if (p_image->is_compressed()) { + ERR_PRINT("Texture '" + texture->path + "' is required to be a power of 2 because it uses either mipmaps or repeat, so it was decompressed. This will hurt performance and memory usage."); + } + + if (img == p_image) { + img = img->duplicate(); + } + img->resize_to_po2(false); + } + + if (config->shrink_textures_x2 && (p_image->has_mipmaps() || !p_image->is_compressed()) && !(texture->flags & TEXTURE_FLAG_USED_FOR_STREAMING)) { + texture->alloc_height = MAX(1, texture->alloc_height / 2); + texture->alloc_width = MAX(1, texture->alloc_width / 2); + + if (texture->alloc_width == img->get_width() / 2 && texture->alloc_height == img->get_height() / 2) { + img->shrink_x2(); + } else if (img->get_format() <= Image::FORMAT_RGBA8) { + img->resize(texture->alloc_width, texture->alloc_height, Image::INTERPOLATE_BILINEAR); + } + } + + GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D; + + texture->data_size = img->get_data().size(); + Vector<uint8_t> read = img->get_data(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + texture->ignore_mipmaps = compressed && !img->has_mipmaps(); + + // set filtering and repeat state + _texture_set_state_from_flags(texture); + + //set swizle for older format compatibility +#ifdef GLES_OVER_GL + switch (texture->format) { + case Image::FORMAT_L8: { + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE); + + } break; + case Image::FORMAT_LA8: { + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_GREEN); + } break; + default: { + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); + + } break; + } +#endif + + int mipmaps = ((texture->flags & TEXTURE_FLAG_MIPMAPS) && img->has_mipmaps()) ? img->get_mipmap_count() + 1 : 1; + + int w = img->get_width(); + int h = img->get_height(); + + int tsize = 0; + + for (int i = 0; i < mipmaps; i++) { + int size, ofs; + img->get_mipmap_offset_and_size(i, ofs, size); + + if (compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int bw = w; + int bh = h; + + glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (texture->flags & TEXTURE_FLAG_USED_FOR_STREAMING) { + glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + } else { + glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + } + } + + tsize += size; + + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + } + + // info.texture_mem -= texture->total_data_size; // TODO make this work again!! + texture->total_data_size = tsize; + // info.texture_mem += texture->total_data_size; // TODO make this work again!! + + // printf("texture: %i x %i - size: %i - total: %i\n", texture->width, texture->height, tsize, info.texture_mem); + + texture->stored_cube_sides |= (1 << p_layer); + + if ((texture->flags & TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (texture->type != RenderingDevice::TEXTURE_TYPE_CUBE || texture->stored_cube_sides == (1 << 6) - 1)) { + //generate mipmaps if they were requested and the image does not contain them + glGenerateMipmap(texture->target); + } + + texture->mipmaps = mipmaps; +} + +void TextureStorage::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer) { + // TODO + ERR_PRINT("Not implemented (ask Karroffel to do it :p)"); +} + +/* +Ref<Image> TextureStorage::texture_get_data(RID p_texture, int p_layer) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, Ref<Image>()); + ERR_FAIL_COND_V(!texture->active, Ref<Image>()); + ERR_FAIL_COND_V(texture->data_size == 0 && !texture->render_target, Ref<Image>()); + + if (texture->type == RS::TEXTURE_TYPE_CUBEMAP && p_layer < 6 && p_layer >= 0 && !texture->images[p_layer].is_null()) { + return texture->images[p_layer]; + } + +#ifdef GLES_OVER_GL + + Image::Format real_format; + GLenum gl_format; + GLenum gl_internal_format; + GLenum gl_type; + bool compressed; + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, false); + + PoolVector<uint8_t> data; + + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1); + + data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers + PoolVector<uint8_t>::Write wb = data.write(); + + glActiveTexture(GL_TEXTURE0); + + glBindTexture(texture->target, texture->tex_id); + + 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, real_format, i); + + if (texture->compressed) { + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glGetCompressedTexImage(texture->target, i, &wb[ofs]); + } else { + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glGetTexImage(texture->target, i, texture->gl_format_cache, texture->gl_type_cache, &wb[ofs]); + } + } + + wb.release(); + + data.resize(data_size); + + Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, texture->mipmaps > 1, real_format, data)); + + return Ref<Image>(img); +#else + + Image::Format real_format; + GLenum gl_format; + GLenum gl_internal_format; + GLenum gl_type; + bool compressed; + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, texture->resize_to_po2); + + PoolVector<uint8_t> data; + + int 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 + PoolVector<uint8_t>::Write wb = data.write(); + + GLuint temp_framebuffer; + glGenFramebuffers(1, &temp_framebuffer); + + GLuint temp_color_texture; + glGenTextures(1, &temp_color_texture); + + glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer); + + glBindTexture(GL_TEXTURE_2D, temp_color_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temp_color_texture, 0); + + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + glColorMask(1, 1, 1, 1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->tex_id); + + glViewport(0, 0, texture->alloc_width, texture->alloc_height); + + shaders.copy.bind(); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + bind_quad_array(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &wb[0]); + + glDeleteTextures(1, &temp_color_texture); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &temp_framebuffer); + + wb.release(); + + data.resize(data_size); + + Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data)); + if (!texture->compressed) { + img->convert(real_format); + } + + return Ref<Image>(img); + +#endif +} +*/ + +void TextureStorage::texture_set_flags(RID p_texture, uint32_t p_flags) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + bool had_mipmaps = texture->flags & TEXTURE_FLAG_MIPMAPS; + + texture->flags = p_flags; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + // set filtering and repeat state + _texture_set_state_from_flags(texture); + + if ((texture->flags & TEXTURE_FLAG_MIPMAPS) && !texture->ignore_mipmaps) { + if (!had_mipmaps && texture->mipmaps == 1) { + glGenerateMipmap(texture->target); + } + } +} + +uint32_t TextureStorage::texture_get_flags(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->flags; +} + +Image::Format TextureStorage::texture_get_format(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, Image::FORMAT_L8); + + return texture->format; +} + +RenderingDevice::TextureType TextureStorage::texture_get_type(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, RenderingDevice::TEXTURE_TYPE_2D); + + return texture->type; +} + +uint32_t TextureStorage::texture_get_texid(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->tex_id; +} + +uint32_t TextureStorage::texture_get_width(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->width; +} + +uint32_t TextureStorage::texture_get_height(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->height; +} + +uint32_t TextureStorage::texture_get_depth(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->depth; +} + +void TextureStorage::texture_bind(RID p_texture, uint32_t p_texture_no) { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND(!texture); + + glActiveTexture(GL_TEXTURE0 + p_texture_no); + glBindTexture(texture->target, texture->tex_id); +} + +void TextureStorage::texture_set_shrink_all_x2_on_set_data(bool p_enable) { + Config::get_singleton()->shrink_textures_x2 = p_enable; +} + +RID TextureStorage::texture_create_radiance_cubemap(RID p_source, int p_resolution) const { + return RID(); +} + +void TextureStorage::textures_keep_original(bool p_enable) { + Config::get_singleton()->keep_original_textures = p_enable; +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h new file mode 100644 index 0000000000..8a921fbe10 --- /dev/null +++ b/drivers/gles3/storage/texture_storage.h @@ -0,0 +1,389 @@ +/*************************************************************************/ +/* texture_storage.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEXTURE_STORAGE_GLES3_H +#define TEXTURE_STORAGE_GLES3_H + +#ifdef GLES3_ENABLED + +#include "canvas_texture_storage.h" +#include "config.h" +#include "core/os/os.h" +#include "core/templates/rid_owner.h" +#include "render_target_storage.h" +#include "servers/rendering/storage/texture_storage.h" + +namespace GLES3 { + +#define _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + +#define _EXT_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define _EXT_COMPRESSED_RED_RGTC1 0x8DBB +#define _EXT_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define _EXT_COMPRESSED_RG_RGTC2 0x8DBD +#define _EXT_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define _EXT_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define _EXT_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#define _EXT_ETC1_RGB8_OES 0x8D64 + +#define _EXT_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define _EXT_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F + +#define _GL_TEXTURE_EXTERNAL_OES 0x8D65 + +#ifdef GLES_OVER_GL +#define _GL_HALF_FLOAT_OES 0x140B +#else +#define _GL_HALF_FLOAT_OES 0x8D61 +#endif + +#define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F + +#define _RED_OES 0x1903 + +#define _DEPTH_COMPONENT24_OES 0x81A6 + +#ifndef GLES_OVER_GL +#define glClearDepth glClearDepthf +#endif //!GLES_OVER_GL + +enum OpenGLTextureFlags { + TEXTURE_FLAG_MIPMAPS = 1, /// Enable automatic mipmap generation - when available + TEXTURE_FLAG_REPEAT = 2, /// Repeat texture (Tiling), otherwise Clamping + TEXTURE_FLAG_FILTER = 4, /// Create texture with linear (or available) filter + TEXTURE_FLAG_ANISOTROPIC_FILTER = 8, + TEXTURE_FLAG_CONVERT_TO_LINEAR = 16, + TEXTURE_FLAG_MIRRORED_REPEAT = 32, /// Repeat texture, with alternate sections mirrored + TEXTURE_FLAG_USED_FOR_STREAMING = 2048, + TEXTURE_FLAGS_DEFAULT = TEXTURE_FLAG_REPEAT | TEXTURE_FLAG_MIPMAPS | TEXTURE_FLAG_FILTER +}; + +struct Texture { + RID self; + + Texture *proxy; + Set<Texture *> proxy_owners; + + String path; + uint32_t flags; + int width, height, depth; + int alloc_width, alloc_height; + Image::Format format; + RenderingDevice::TextureType type; + + GLenum target; + GLenum gl_format_cache; + GLenum gl_internal_format_cache; + GLenum gl_type_cache; + + int data_size; + int total_data_size; + bool ignore_mipmaps; + + bool compressed; + + bool srgb; + + int mipmaps; + + bool resize_to_po2; + + bool active; + GLenum tex_id; + + uint16_t stored_cube_sides; + + RenderTarget *render_target; + + Vector<Ref<Image>> images; + + bool redraw_if_visible; + + RS::TextureDetectCallback detect_3d; + void *detect_3d_ud; + + RS::TextureDetectCallback detect_srgb; + void *detect_srgb_ud; + + RS::TextureDetectCallback detect_normal; + void *detect_normal_ud; + + CanvasTexture *canvas_texture = nullptr; + + // some silly opengl shenanigans where + // texture coords start from bottom left, means we need to draw render target textures upside down + // to be compatible with vulkan etc. + bool is_upside_down() const { + if (proxy) { + return proxy->is_upside_down(); + } + + return render_target != nullptr; + } + + Texture() { + create(); + } + + _ALWAYS_INLINE_ Texture *get_ptr() { + if (proxy) { + return proxy; //->get_ptr(); only one level of indirection, else not inlining possible. + } else { + return this; + } + } + + ~Texture() { + destroy(); + + if (tex_id != 0) { + glDeleteTextures(1, &tex_id); + } + } + + void copy_from(const Texture &o) { + proxy = o.proxy; + flags = o.flags; + width = o.width; + height = o.height; + alloc_width = o.alloc_width; + alloc_height = o.alloc_height; + format = o.format; + type = o.type; + target = o.target; + data_size = o.data_size; + total_data_size = o.total_data_size; + ignore_mipmaps = o.ignore_mipmaps; + compressed = o.compressed; + mipmaps = o.mipmaps; + resize_to_po2 = o.resize_to_po2; + active = o.active; + tex_id = o.tex_id; + stored_cube_sides = o.stored_cube_sides; + render_target = o.render_target; + redraw_if_visible = o.redraw_if_visible; + detect_3d = o.detect_3d; + detect_3d_ud = o.detect_3d_ud; + detect_srgb = o.detect_srgb; + detect_srgb_ud = o.detect_srgb_ud; + detect_normal = o.detect_normal; + detect_normal_ud = o.detect_normal_ud; + + images.clear(); + } + + void create() { + proxy = nullptr; + flags = 0; + width = 0; + height = 0; + alloc_width = 0; + alloc_height = 0; + format = Image::FORMAT_L8; + type = RenderingDevice::TEXTURE_TYPE_2D; + target = 0; + data_size = 0; + total_data_size = 0; + ignore_mipmaps = false; + compressed = false; + mipmaps = 0; + resize_to_po2 = false; + active = false; + tex_id = 0; + stored_cube_sides = 0; + render_target = nullptr; + redraw_if_visible = false; + detect_3d = nullptr; + detect_3d_ud = nullptr; + detect_srgb = nullptr; + detect_srgb_ud = nullptr; + detect_normal = nullptr; + detect_normal_ud = nullptr; + } + void destroy() { + images.clear(); + + for (Set<Texture *>::Element *E = proxy_owners.front(); E; E = E->next()) { + E->get()->proxy = nullptr; + } + + if (proxy) { + proxy->proxy_owners.erase(this); + } + } + + // texture state + void GLSetFilter(GLenum p_target, RS::CanvasItemTextureFilter p_filter) { + if (p_filter == state_filter) { + return; + } + state_filter = p_filter; + GLint pmin = GL_LINEAR; // param min + GLint pmag = GL_LINEAR; // param mag + switch (state_filter) { + default: { + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: { + pmin = GL_LINEAR_MIPMAP_LINEAR; + pmag = GL_LINEAR; + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: { + pmin = GL_NEAREST; + pmag = GL_NEAREST; + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: { + pmin = GL_NEAREST_MIPMAP_NEAREST; + pmag = GL_NEAREST; + } break; + } + glTexParameteri(p_target, GL_TEXTURE_MIN_FILTER, pmin); + glTexParameteri(p_target, GL_TEXTURE_MAG_FILTER, pmag); + } + void GLSetRepeat(GLenum p_target, RS::CanvasItemTextureRepeat p_repeat) { + if (p_repeat == state_repeat) { + return; + } + state_repeat = p_repeat; + GLint prep = GL_CLAMP_TO_EDGE; // parameter repeat + switch (state_repeat) { + default: { + } break; + case RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: { + prep = GL_REPEAT; + } break; + case RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: { + prep = GL_MIRRORED_REPEAT; + } break; + } + glTexParameteri(p_target, GL_TEXTURE_WRAP_S, prep); + glTexParameteri(p_target, GL_TEXTURE_WRAP_T, prep); + } + +private: + RS::CanvasItemTextureFilter state_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; + RS::CanvasItemTextureRepeat state_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; +}; + +class TextureStorage : public RendererTextureStorage { +private: + static TextureStorage *singleton; + + Thread::ID _main_thread_id = 0; + bool _is_main_thread(); + + mutable RID_PtrOwner<Texture> texture_owner; + + Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, 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; + void _texture_set_state_from_flags(Texture *p_tex); + + void texture_set_proxy(RID p_texture, RID p_proxy); + +public: + static TextureStorage *get_singleton(); + + TextureStorage(); + virtual ~TextureStorage(); + + Texture *get_texture(RID p_rid) { return texture_owner.get_or_null(p_rid); }; + bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); }; + RID make_rid(Texture *p_texture) { return texture_owner.make_rid(p_texture); }; + + void set_main_thread_id(Thread::ID p_id); + + virtual bool can_create_resources_async() const override; + + RID texture_create(); + void _texture_allocate_internal(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, RenderingDevice::TextureType p_type, uint32_t p_flags = TEXTURE_FLAGS_DEFAULT); + + virtual RID texture_allocate() override; + virtual void texture_free(RID p_rid) override; + + virtual void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override; + virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override; + virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override; + virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent + + virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override; + virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override{}; + virtual void texture_proxy_update(RID p_proxy, RID p_base) override{}; + + //these two APIs can be used together or in combination with the others. + virtual void texture_2d_placeholder_initialize(RID p_texture) override; + virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override; + virtual void texture_3d_placeholder_initialize(RID p_texture) override; + + virtual Ref<Image> texture_2d_get(RID p_texture) const override; + virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const override { return Ref<Image>(); }; + virtual Vector<Ref<Image>> texture_3d_get(RID p_texture) const override { return Vector<Ref<Image>>(); }; + + virtual void texture_replace(RID p_texture, RID p_by_texture) override; + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) override; + + virtual void texture_set_path(RID p_texture, const String &p_path) override; + virtual String texture_get_path(RID p_texture) const override; + + virtual void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override; + void texture_set_detect_srgb_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata); + virtual void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override; + virtual void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) override{}; + + virtual void texture_debug_usage(List<RS::TextureInfo> *r_info) override; + + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) override; + + virtual Size2 texture_size_with_proxy(RID p_proxy) override; + + void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); + void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer = 0); + //Ref<Image> texture_get_data(RID p_texture, int p_layer = 0) const; + void texture_set_flags(RID p_texture, uint32_t p_flags); + uint32_t texture_get_flags(RID p_texture) const; + Image::Format texture_get_format(RID p_texture) const; + RenderingDevice::TextureType texture_get_type(RID p_texture) const; + uint32_t texture_get_texid(RID p_texture) const; + uint32_t texture_get_width(RID p_texture) const; + uint32_t texture_get_height(RID p_texture) const; + uint32_t texture_get_depth(RID p_texture) const; + void texture_bind(RID p_texture, uint32_t p_texture_no); + void texture_set_shrink_all_x2_on_set_data(bool p_enable); + RID texture_create_radiance_cubemap(RID p_source, int p_resolution = -1) const; + void textures_keep_original(bool p_enable); +}; + +} // namespace GLES3 + +#endif // !GLES3_ENABLED + +#endif // !TEXTURE_STORAGE_GLES3_H diff --git a/drivers/dummy/texture_loader_dummy.cpp b/drivers/gles3/texture_loader_gles3.cpp index f148e42845..f8d4cfdc61 100644 --- a/drivers/dummy/texture_loader_dummy.cpp +++ b/drivers/gles3/texture_loader_gles3.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* texture_loader_dummy.cpp */ +/* texture_loader_gles3.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,14 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "texture_loader_dummy.h" +#include "texture_loader_gles3.h" -#include "core/os/file_access.h" +#ifdef GLES3_ENABLED + +#include "core/io/file_access.h" #include "core/string/print_string.h" #include <string.h> -RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { +RES ResourceFormatGLES2Texture::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { unsigned int width = 8; unsigned int height = 8; @@ -60,13 +62,14 @@ RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_origi Ref<ImageTexture> texture = memnew(ImageTexture); texture->create_from_image(img); - if (r_error) + if (r_error) { *r_error = OK; + } return texture; } -void ResourceFormatDummyTexture::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceFormatGLES2Texture::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("bmp"); p_extensions->push_back("dds"); p_extensions->push_back("exr"); @@ -77,16 +80,15 @@ void ResourceFormatDummyTexture::get_recognized_extensions(List<String> *p_exten p_extensions->push_back("png"); p_extensions->push_back("pvr"); p_extensions->push_back("svg"); - p_extensions->push_back("svgz"); p_extensions->push_back("tga"); p_extensions->push_back("webp"); } -bool ResourceFormatDummyTexture::handles_type(const String &p_type) const { +bool ResourceFormatGLES2Texture::handles_type(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Texture2D"); } -String ResourceFormatDummyTexture::get_resource_type(const String &p_path) const { +String ResourceFormatGLES2Texture::get_resource_type(const String &p_path) const { String extension = p_path.get_extension().to_lower(); if ( extension == "bmp" || @@ -99,7 +101,6 @@ String ResourceFormatDummyTexture::get_resource_type(const String &p_path) const extension == "png" || extension == "pvr" || extension == "svg" || - extension == "svgz" || extension == "tga" || extension == "webp") { return "ImageTexture"; @@ -107,3 +108,5 @@ String ResourceFormatDummyTexture::get_resource_type(const String &p_path) const return ""; } + +#endif diff --git a/drivers/dummy/texture_loader_dummy.h b/drivers/gles3/texture_loader_gles3.h index 00e6b9cc53..54ddf80a96 100644 --- a/drivers/dummy/texture_loader_dummy.h +++ b/drivers/gles3/texture_loader_gles3.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* texture_loader_dummy.h */ +/* texture_loader_gles3.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,20 +28,24 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TEXTURE_LOADER_DUMMY_H -#define TEXTURE_LOADER_DUMMY_H +#ifndef TEXTURE_LOADER_OPENGL_H +#define TEXTURE_LOADER_OPENGL_H + +#ifdef GLES3_ENABLED #include "core/io/resource_loader.h" #include "scene/resources/texture.h" -class ResourceFormatDummyTexture : public ResourceFormatLoader { +class ResourceFormatGLES2Texture : public ResourceFormatLoader { public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; - virtual ~ResourceFormatDummyTexture() {} + virtual ~ResourceFormatGLES2Texture() {} }; -#endif // TEXTURE_LOADER_DUMMY_H +#endif // GLES3_ENABLED + +#endif // TEXTURE_LOADER_OPENGL_H diff --git a/drivers/png/SCsub b/drivers/png/SCsub index 26508dc612..39d296e7cf 100644 --- a/drivers/png/SCsub +++ b/drivers/png/SCsub @@ -36,7 +36,8 @@ if env["builtin_libpng"]: # Currently .ASM filter_neon.S does not compile on NT. import os - use_neon = "neon_enabled" in env and env["neon_enabled"] and os.name != "nt" + # Enable ARM NEON instructions on 32-bit Android to compile more optimized code. + use_neon = "android_arch" in env and env["android_arch"] == "armv7" and os.name != "nt" if use_neon: env_png.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT", 2)]) else: diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index 854c6706e6..46e271f9c9 100644 --- a/drivers/png/image_loader_png.cpp +++ b/drivers/png/image_loader_png.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,7 +37,7 @@ #include <string.h> Error ImageLoaderPNG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) { - const size_t buffer_size = f->get_len(); + const uint64_t buffer_size = f->get_length(); Vector<uint8_t> file_buffer; Error err = file_buffer.resize(buffer_size); if (err) { @@ -59,7 +59,7 @@ void ImageLoaderPNG::get_recognized_extensions(List<String> *p_extensions) const Ref<Image> ImageLoaderPNG::load_mem_png(const uint8_t *p_png, int p_size) { Ref<Image> img; - img.instance(); + img.instantiate(); // the value of p_force_linear does not matter since it only applies to 16 bit Error err = PNGDriverCommon::png_to_image(p_png, p_size, false, img); @@ -88,7 +88,7 @@ Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) { { // must be closed before call to image_to_png uint8_t *writer = out_buffer.ptrw(); - copymem(writer, "PNG ", 4); + memcpy(writer, "PNG ", 4); } Error err = PNGDriverCommon::image_to_png(p_image, out_buffer); @@ -101,6 +101,6 @@ Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) { ImageLoaderPNG::ImageLoaderPNG() { Image::_png_mem_loader_func = load_mem_png; - Image::lossless_unpacker = lossless_unpack_png; - Image::lossless_packer = lossless_pack_png; + Image::png_unpacker = lossless_unpack_png; + Image::png_packer = lossless_pack_png; } diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h index b4a58616f6..af3bcd5b66 100644 --- a/drivers/png/image_loader_png.h +++ b/drivers/png/image_loader_png.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/png/png_driver_common.cpp b/drivers/png/png_driver_common.cpp index 9e848a2253..bc4bb3782b 100644 --- a/drivers/png/png_driver_common.cpp +++ b/drivers/png/png_driver_common.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -60,7 +60,7 @@ static bool check_error(const png_image &image) { Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, Ref<Image> p_image) { png_image png_img; - zeromem(&png_img, sizeof(png_img)); + memset(&png_img, 0, sizeof(png_img)); png_img.version = PNG_IMAGE_VERSION; // fetch image properties @@ -134,7 +134,7 @@ Error image_to_png(const Ref<Image> &p_image, Vector<uint8_t> &p_buffer) { ERR_FAIL_COND_V(source_image->is_compressed(), FAILED); png_image png_img; - zeromem(&png_img, sizeof(png_img)); + memset(&png_img, 0, sizeof(png_img)); png_img.version = PNG_IMAGE_VERSION; png_img.width = source_image->get_width(); png_img.height = source_image->get_height(); diff --git a/drivers/png/png_driver_common.h b/drivers/png/png_driver_common.h index 003b587913..62302bbdbb 100644 --- a/drivers/png/png_driver_common.h +++ b/drivers/png/png_driver_common.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/png/resource_saver_png.cpp b/drivers/png/resource_saver_png.cpp index f47fc403cc..ca84fb6be9 100644 --- a/drivers/png/resource_saver_png.cpp +++ b/drivers/png/resource_saver_png.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,8 +30,8 @@ #include "resource_saver_png.h" +#include "core/io/file_access.h" #include "core/io/image.h" -#include "core/os/file_access.h" #include "drivers/png/png_driver_common.h" #include "scene/resources/texture.h" @@ -41,12 +41,12 @@ Error ResourceSaverPNG::save(const String &p_path, const RES &p_resource, uint32 ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as PNG."); ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as PNG."); - Ref<Image> img = texture->get_data(); + Ref<Image> img = texture->get_image(); Error err = save_image(p_path, img); return err; -}; +} Error ResourceSaverPNG::save_image(const String &p_path, const Ref<Image> &p_img) { Vector<uint8_t> buffer; @@ -89,4 +89,4 @@ void ResourceSaverPNG::get_recognized_extensions(const RES &p_resource, List<Str ResourceSaverPNG::ResourceSaverPNG() { Image::save_png_func = &save_image; Image::save_png_buffer_func = &save_image_to_buffer; -}; +} diff --git a/drivers/png/resource_saver_png.h b/drivers/png/resource_saver_png.h index c924438224..f39e52c7ec 100644 --- a/drivers/png/resource_saver_png.h +++ b/drivers/png/resource_saver_png.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 5e87bc019b..ef9bbc3483 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,19 +34,30 @@ #include "core/config/project_settings.h" #include "core/os/os.h" +#include "core/version.h" + +#ifdef ALSAMIDI_ENABLED +#include "drivers/alsa/asound-so_wrap.h" +#endif void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; switch (pa_context_get_state(c)) { case PA_CONTEXT_TERMINATED: + print_verbose("PulseAudio: context terminated"); + ad->pa_ready = -1; + break; case PA_CONTEXT_FAILED: + print_verbose("PulseAudio: context failed"); ad->pa_ready = -1; break; case PA_CONTEXT_READY: + print_verbose("PulseAudio: context ready"); ad->pa_ready = 1; break; default: + print_verbose("PulseAudio: context other"); // TODO: Check if we want to handle some of the other // PA context states like PA_CONTEXT_UNCONNECTED. break; @@ -61,6 +72,13 @@ void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l return; } + // If eol is set to a negative number there's an error. + if (eol < 0) { + ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c)))); + ad->pa_status--; + return; + } + ad->pa_map = l->channel_map; ad->pa_status++; } @@ -73,6 +91,13 @@ void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_inf return; } + // If eol is set to a negative number there's an error. + if (eol < 0) { + ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c)))); + ad->pa_status--; + return; + } + ad->pa_rec_map = l->channel_map; ad->pa_status++; } @@ -86,7 +111,7 @@ void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_inf ad->pa_status++; } -void AudioDriverPulseAudio::detect_channels(bool capture) { +Error AudioDriverPulseAudio::detect_channels(bool capture) { pa_channel_map_init_stereo(capture ? &pa_rec_map : &pa_map); String device = capture ? capture_device_name : device_name; @@ -104,7 +129,8 @@ void AudioDriverPulseAudio::detect_channels(bool capture) { pa_operation_unref(pa_op); } else { - ERR_PRINT("pa_context_get_server_info error"); + ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(pa_ctx)))); + return FAILED; } } @@ -114,6 +140,7 @@ void AudioDriverPulseAudio::detect_channels(bool capture) { } else { strcpy(dev, device.utf8().get_data()); } + print_verbose("PulseAudio: Detecting channels for device: " + String(dev)); // Now using the device name get the amount of channels pa_status = 0; @@ -133,6 +160,10 @@ void AudioDriverPulseAudio::detect_channels(bool capture) { } pa_operation_unref(pa_op); + + if (pa_status == -1) { + return FAILED; + } } else { if (capture) { ERR_PRINT("pa_context_get_source_info_by_name error"); @@ -140,6 +171,8 @@ void AudioDriverPulseAudio::detect_channels(bool capture) { ERR_PRINT("pa_context_get_sink_info_by_name error"); } } + + return OK; } Error AudioDriverPulseAudio::init_device() { @@ -156,7 +189,13 @@ Error AudioDriverPulseAudio::init_device() { // Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal, // if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels // will have the amount of channels Godot is using (in this case it's pa_map.channels + 1) - detect_channels(); + Error err = detect_channels(); + if (err != OK) { + // This most likely means there are no sinks. + ERR_PRINT("PulseAudio: init device failed to detect number of output channels"); + return err; + } + switch (pa_map.channels) { case 1: // Mono case 3: // Surround 2.1 @@ -173,7 +212,7 @@ Error AudioDriverPulseAudio::init_device() { break; default: - WARN_PRINT("PulseAudio: Unsupported number of channels: " + itos(pa_map.channels)); + WARN_PRINT("PulseAudio: Unsupported number of output channels: " + itos(pa_map.channels)); pa_channel_map_init_stereo(&pa_map); channels = 2; break; @@ -183,8 +222,8 @@ Error AudioDriverPulseAudio::init_device() { buffer_frames = closest_power_of_2(latency * mix_rate / 1000); pa_buffer_size = buffer_frames * pa_map.channels; - print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " channels"); - print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); + print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels"); + print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated output latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); pa_sample_spec spec; spec.format = PA_SAMPLE_S16LE; @@ -238,6 +277,10 @@ Error AudioDriverPulseAudio::init() { #else int dylibloader_verbose = 0; #endif +#ifdef ALSAMIDI_ENABLED + // If using PulseAudio with ALSA MIDI, we need to initialize ALSA as well + initialize_asound(dylibloader_verbose); +#endif if (initialize_pulse(dylibloader_verbose)) { return ERR_CANT_OPEN; } @@ -251,7 +294,17 @@ Error AudioDriverPulseAudio::init() { pa_ml = pa_mainloop_new(); ERR_FAIL_COND_V(pa_ml == nullptr, ERR_CANT_OPEN); - pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), "Godot"); + String context_name; + if (Engine::get_singleton()->is_editor_hint()) { + context_name = VERSION_NAME " Editor"; + } else { + context_name = GLOBAL_GET("application/config/name"); + if (context_name.is_empty()) { + context_name = VERSION_NAME " Project"; + } + } + + pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), context_name.utf8().ptr()); ERR_FAIL_COND_V(pa_ctx == nullptr, ERR_CANT_OPEN); pa_ready = 0; @@ -294,10 +347,8 @@ Error AudioDriverPulseAudio::init() { return ERR_CANT_OPEN; } - Error err = init_device(); - if (err == OK) { - thread.start(AudioDriverPulseAudio::thread_func, this); - } + init_device(); + thread.start(AudioDriverPulseAudio::thread_func, this); return OK; } @@ -331,7 +382,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata; unsigned int write_ofs = 0; size_t avail_bytes = 0; - uint32_t default_device_msec = OS::get_singleton()->get_ticks_msec(); + uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec(); while (!ad->exit_thread) { size_t read_bytes = 0; @@ -342,15 +393,15 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { ad->start_counting_ticks(); if (!ad->active) { - for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { - ad->samples_out.write[i] = 0; - } + ad->samples_out.fill(0); } else { ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); + int16_t *out_ptr = ad->samples_out.ptrw(); + if (ad->channels == ad->pa_map.channels) { for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { - ad->samples_out.write[i] = ad->samples_in[i] >> 16; + out_ptr[i] = ad->samples_in[i] >> 16; } } else { // Uneven amount of channels @@ -359,11 +410,11 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { for (unsigned int i = 0; i < ad->buffer_frames; i++) { for (int j = 0; j < ad->pa_map.channels - 1; j++) { - ad->samples_out.write[out_idx++] = ad->samples_in[in_idx++] >> 16; + out_ptr[out_idx++] = ad->samples_in[in_idx++] >> 16; } uint32_t l = ad->samples_in[in_idx++] >> 16; uint32_t r = ad->samples_in[in_idx++] >> 16; - ad->samples_out.write[out_idx++] = (l + r) / 2; + out_ptr[out_idx++] = (l + r) / 2; } } } @@ -423,7 +474,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { // If we're using the default device check that the current device is still the default if (ad->device_name == "Default") { - uint32_t msec = OS::get_singleton()->get_ticks_msec(); + uint64_t msec = OS::get_singleton()->get_ticks_msec(); if (msec > (default_device_msec + 1000)) { String old_default_device = ad->default_device; @@ -441,7 +492,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { pa_operation_unref(pa_op); } else { - ERR_PRINT("pa_context_get_server_info error"); + ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(ad->pa_ctx)))); } if (old_default_device != ad->default_device) { @@ -649,6 +700,8 @@ Error AudioDriverPulseAudio::capture_init_device() { break; } + print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels"); + pa_sample_spec spec; spec.format = PA_SAMPLE_S16LE; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index fa9b573d94..af96489972 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -89,7 +89,7 @@ class AudioDriverPulseAudio : public AudioDriver { Error capture_init_device(); void capture_finish_device(); - void detect_channels(bool capture = false); + Error detect_channels(bool capture = false); static void thread_func(void *p_udata); diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp index 18262c74c4..1047e89e85 100644 --- a/drivers/register_driver_types.cpp +++ b/drivers/register_driver_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,6 +30,7 @@ #include "register_driver_types.h" +#include "core/extension/native_extension_manager.h" #include "drivers/png/image_loader_png.h" #include "drivers/png/resource_saver_png.h" @@ -40,7 +41,7 @@ void register_core_driver_types() { image_loader_png = memnew(ImageLoaderPNG); ImageLoader::add_image_format_loader(image_loader_png); - resource_saver_png.instance(); + resource_saver_png.instantiate(); ResourceSaver::add_resource_format_saver(resource_saver_png); } @@ -54,7 +55,9 @@ void unregister_core_driver_types() { } void register_driver_types() { + NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_DRIVER); } void unregister_driver_types() { + NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_DRIVER); } diff --git a/drivers/register_driver_types.h b/drivers/register_driver_types.h index 607aa91cb8..c008d93185 100644 --- a/drivers/register_driver_types.h +++ b/drivers/register_driver_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index eda929850c..af47173b41 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -71,7 +71,7 @@ Error DirAccessUnix::list_dir_begin() { bool DirAccessUnix::file_exists(String p_file) { GLOBAL_LOCK_FUNCTION - if (p_file.is_rel_path()) { + if (p_file.is_relative_path()) { p_file = current_dir.plus_file(p_file); } @@ -90,7 +90,7 @@ bool DirAccessUnix::file_exists(String p_file) { bool DirAccessUnix::dir_exists(String p_dir) { GLOBAL_LOCK_FUNCTION - if (p_dir.is_rel_path()) { + if (p_dir.is_relative_path()) { p_dir = get_current_dir().plus_file(p_dir); } @@ -102,8 +102,30 @@ bool DirAccessUnix::dir_exists(String p_dir) { return (success && S_ISDIR(flags.st_mode)); } +bool DirAccessUnix::is_readable(String p_dir) { + GLOBAL_LOCK_FUNCTION + + if (p_dir.is_relative_path()) { + p_dir = get_current_dir().plus_file(p_dir); + } + + p_dir = fix_path(p_dir); + return (access(p_dir.utf8().get_data(), R_OK) == 0); +} + +bool DirAccessUnix::is_writable(String p_dir) { + GLOBAL_LOCK_FUNCTION + + if (p_dir.is_relative_path()) { + p_dir = get_current_dir().plus_file(p_dir); + } + + p_dir = fix_path(p_dir); + return (access(p_dir.utf8().get_data(), W_OK) == 0); +} + uint64_t DirAccessUnix::get_modified_time(String p_file) { - if (p_file.is_rel_path()) { + if (p_file.is_relative_path()) { p_file = current_dir.plus_file(p_file); } @@ -116,9 +138,9 @@ uint64_t DirAccessUnix::get_modified_time(String p_file) { return flags.st_mtime; } else { ERR_FAIL_V(0); - }; + } return 0; -}; +} String DirAccessUnix::get_next() { if (!dir_stream) { @@ -194,6 +216,8 @@ static bool _filter_drive(struct mntent *mnt) { #endif static void _get_drives(List<String> *list) { + list->push_back("/"); + #if defined(HAVE_MNTENT) && defined(X11_ENABLED) // Check /etc/mtab for the list of mounted partitions FILE *mtab = setmntent("/etc/mtab", "r"); @@ -204,8 +228,9 @@ static void _get_drives(List<String> *list) { while (getmntent_r(mtab, &mnt, strings, sizeof(strings))) { if (mnt.mnt_dir != nullptr && _filter_drive(&mnt)) { // Avoid duplicates - if (!list->find(mnt.mnt_dir)) { - list->push_back(mnt.mnt_dir); + String name = String::utf8(mnt.mnt_dir); + if (!list->find(name)) { + list->push_back(name); } } } @@ -218,8 +243,9 @@ static void _get_drives(List<String> *list) { const char *home = getenv("HOME"); if (home) { // Only add if it's not a duplicate - if (!list->find(home)) { - list->push_back(home); + String home_name = String::utf8(home); + if (!list->find(home_name)) { + list->push_back(home_name); } // Check $HOME/.config/gtk-3.0/bookmarks @@ -232,7 +258,7 @@ static void _get_drives(List<String> *list) { // Parse only file:// links if (strncmp(string, "file://", 7) == 0) { // Strip any unwanted edges on the strings and push_back if it's not a duplicate - String fpath = String(string + 7).strip_edges().split_spaces()[0].uri_decode(); + String fpath = String::utf8(string + 7).strip_edges().split_spaces()[0].uri_decode(); if (!list->find(fpath)) { list->push_back(fpath); } @@ -262,6 +288,20 @@ String DirAccessUnix::get_drive(int p_drive) { return list[p_drive]; } +int DirAccessUnix::get_current_drive() { + int drive = 0; + int max_length = -1; + const String path = get_current_dir().to_lower(); + for (int i = 0; i < get_drive_count(); i++) { + const String d = get_drive(i).to_lower(); + if (max_length < d.length() && path.begins_with(d)) { + max_length = d.length(); + drive = i; + } + } + return drive; +} + bool DirAccessUnix::drives_are_shortcuts() { return true; } @@ -269,7 +309,7 @@ bool DirAccessUnix::drives_are_shortcuts() { Error DirAccessUnix::make_dir(String p_dir) { GLOBAL_LOCK_FUNCTION - if (p_dir.is_rel_path()) { + if (p_dir.is_relative_path()) { p_dir = get_current_dir().plus_file(p_dir); } @@ -280,11 +320,11 @@ Error DirAccessUnix::make_dir(String p_dir) { if (success) { return OK; - }; + } if (err == EEXIST) { return ERR_ALREADY_EXISTS; - }; + } return ERR_CANT_CREATE; } @@ -304,7 +344,7 @@ Error DirAccessUnix::change_dir(String p_dir) { // try_dir is the directory we are trying to change into String try_dir = ""; - if (p_dir.is_rel_path()) { + if (p_dir.is_relative_path()) { String next_dir = current_dir.plus_file(p_dir); next_dir = next_dir.simplify_path(); try_dir = next_dir; @@ -318,7 +358,7 @@ Error DirAccessUnix::change_dir(String p_dir) { } String base = _get_root_path(); - if (base != String() && !try_dir.begins_with(base)) { + if (!base.is_empty() && !try_dir.begins_with(base)) { ERR_FAIL_COND_V(getcwd(real_current_dir_name, 2048) == nullptr, ERR_BUG); String new_dir; new_dir.parse_utf8(real_current_dir_name); @@ -336,7 +376,7 @@ Error DirAccessUnix::change_dir(String p_dir) { String DirAccessUnix::get_current_dir(bool p_include_drive) { String base = _get_root_path(); - if (base != "") { + if (!base.is_empty()) { String bd = current_dir.replace_first(base, ""); if (bd.begins_with("/")) { return _get_root_string() + bd.substr(1, bd.length()); @@ -348,13 +388,13 @@ String DirAccessUnix::get_current_dir(bool p_include_drive) { } Error DirAccessUnix::rename(String p_path, String p_new_path) { - if (p_path.is_rel_path()) { + if (p_path.is_relative_path()) { p_path = get_current_dir().plus_file(p_path); } p_path = fix_path(p_path); - if (p_new_path.is_rel_path()) { + if (p_new_path.is_relative_path()) { p_new_path = get_current_dir().plus_file(p_new_path); } @@ -364,7 +404,7 @@ Error DirAccessUnix::rename(String p_path, String p_new_path) { } Error DirAccessUnix::remove(String p_path) { - if (p_path.is_rel_path()) { + if (p_path.is_relative_path()) { p_path = get_current_dir().plus_file(p_path); } @@ -382,19 +422,66 @@ Error DirAccessUnix::remove(String p_path) { } } -size_t DirAccessUnix::get_space_left() { +bool DirAccessUnix::is_link(String p_file) { + if (p_file.is_relative_path()) { + p_file = get_current_dir().plus_file(p_file); + } + + p_file = fix_path(p_file); + + struct stat flags; + if ((lstat(p_file.utf8().get_data(), &flags) != 0)) { + return FAILED; + } + + return S_ISLNK(flags.st_mode); +} + +String DirAccessUnix::read_link(String p_file) { + if (p_file.is_relative_path()) { + p_file = get_current_dir().plus_file(p_file); + } + + p_file = fix_path(p_file); + + char buf[256]; + memset(buf, 0, 256); + ssize_t len = readlink(p_file.utf8().get_data(), buf, sizeof(buf)); + String link; + if (len > 0) { + link.parse_utf8(buf, len); + } + return link; +} + +Error DirAccessUnix::create_link(String p_source, String p_target) { + if (p_target.is_relative_path()) { + p_target = get_current_dir().plus_file(p_target); + } + + p_source = fix_path(p_source); + p_target = fix_path(p_target); + + if (symlink(p_source.utf8().get_data(), p_target.utf8().get_data()) == 0) { + return OK; + } else { + return FAILED; + } +} + +uint64_t DirAccessUnix::get_space_left() { #ifndef NO_STATVFS struct statvfs vfs; if (statvfs(current_dir.utf8().get_data(), &vfs) != 0) { return 0; - }; + } - return vfs.f_bfree * vfs.f_bsize; + return (uint64_t)vfs.f_bavail * (uint64_t)vfs.f_frsize; #else // FIXME: Implement this. return 0; #endif -}; +} String DirAccessUnix::get_filesystem_type() const { return ""; //TODO this should be implemented diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h index b70df1ca02..f90f55605c 100644 --- a/drivers/unix/dir_access_unix.h +++ b/drivers/unix/dir_access_unix.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,7 @@ #if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) -#include "core/os/dir_access.h" +#include "core/io/dir_access.h" #include <dirent.h> #include <sys/stat.h> @@ -63,6 +63,7 @@ public: virtual int get_drive_count(); virtual String get_drive(int p_drive); + virtual int get_current_drive(); virtual bool drives_are_shortcuts(); virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success @@ -71,13 +72,19 @@ public: virtual bool file_exists(String p_file); virtual bool dir_exists(String p_dir); + virtual bool is_readable(String p_dir); + virtual bool is_writable(String p_dir); virtual uint64_t get_modified_time(String p_file); virtual Error rename(String p_path, String p_new_path); virtual Error remove(String p_path); - virtual size_t get_space_left(); + virtual bool is_link(String p_file); + virtual String read_link(String p_file); + virtual Error create_link(String p_source, String p_target); + + virtual uint64_t get_space_left(); virtual String get_filesystem_type() const; diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 6b24a85ff6..ea442ad8bf 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -111,7 +111,7 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { } } - if (is_backup_save_enabled() && (p_mode_flags & WRITE) && !(p_mode_flags & READ)) { + if (is_backup_save_enabled() && (p_mode_flags == WRITE)) { save_path = path; path = path + ".tmp"; } @@ -160,7 +160,7 @@ void FileAccessUnix::close() { close_notification_func(path, flags); } - if (save_path != "") { + if (!save_path.is_empty()) { int rename_error = rename((save_path + ".tmp").utf8().get_data(), save_path.utf8().get_data()); if (rename_error && close_fail_notify) { @@ -184,11 +184,11 @@ String FileAccessUnix::get_path_absolute() const { return path; } -void FileAccessUnix::seek(size_t p_position) { +void FileAccessUnix::seek(uint64_t p_position) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); last_error = OK; - if (fseek(f, p_position, SEEK_SET)) { + if (fseeko(f, p_position, SEEK_SET)) { check_errors(); } } @@ -196,15 +196,15 @@ void FileAccessUnix::seek(size_t p_position) { void FileAccessUnix::seek_end(int64_t p_position) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); - if (fseek(f, p_position, SEEK_END)) { + if (fseeko(f, p_position, SEEK_END)) { check_errors(); } } -size_t FileAccessUnix::get_position() const { +uint64_t FileAccessUnix::get_position() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); - long pos = ftell(f); + int64_t pos = ftello(f); if (pos < 0) { check_errors(); ERR_FAIL_V(0); @@ -212,15 +212,15 @@ size_t FileAccessUnix::get_position() const { return pos; } -size_t FileAccessUnix::get_len() const { +uint64_t FileAccessUnix::get_length() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); - long pos = ftell(f); + int64_t pos = ftello(f); ERR_FAIL_COND_V(pos < 0, 0); - ERR_FAIL_COND_V(fseek(f, 0, SEEK_END), 0); - long size = ftell(f); + ERR_FAIL_COND_V(fseeko(f, 0, SEEK_END), 0); + int64_t size = ftello(f); ERR_FAIL_COND_V(size < 0, 0); - ERR_FAIL_COND_V(fseek(f, pos, SEEK_SET), 0); + ERR_FAIL_COND_V(fseeko(f, pos, SEEK_SET), 0); return size; } @@ -239,12 +239,14 @@ uint8_t FileAccessUnix::get_8() const { return b; } -int FileAccessUnix::get_buffer(uint8_t *p_dst, int p_length) const { +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_COND_V_MSG(!f, -1, "File must be opened before use."); - int read = fread(p_dst, 1, p_length, f); + + uint64_t read = fread(p_dst, 1, p_length, f); check_errors(); return read; -}; +} Error FileAccessUnix::get_error() const { return last_error; @@ -260,10 +262,10 @@ void FileAccessUnix::store_8(uint8_t p_dest) { ERR_FAIL_COND(fwrite(&p_dest, 1, 1, f) != 1); } -void FileAccessUnix::store_buffer(const uint8_t *p_src, int p_length) { +void FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); - ERR_FAIL_COND(!p_src); - ERR_FAIL_COND((int)fwrite(p_src, 1, p_length, f) != p_length); + ERR_FAIL_COND(!p_src && p_length > 0); + ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != p_length); } bool FileAccessUnix::file_exists(const String &p_path) { @@ -283,8 +285,9 @@ bool FileAccessUnix::file_exists(const String &p_path) { return false; } #else - if (_access(filename.utf8().get_data(), 4) == -1) + if (_access(filename.utf8().get_data(), 4) == -1) { return false; + } #endif // See if this is a regular file @@ -305,8 +308,9 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { if (!err) { return flags.st_mtime; } else { - ERR_FAIL_V_MSG(0, "Failed to get modified time for: " + p_file + "."); - }; + print_verbose("Failed to get modified time for: " + p_file + ""); + return 0; + } } uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) { @@ -318,7 +322,7 @@ uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) { return flags.st_mode & 0x7FF; //only permissions } else { ERR_FAIL_V_MSG(0, "Failed to get unix permissions for: " + p_file + "."); - }; + } } Error FileAccessUnix::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 998fad7909..8ebdcd2a2d 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #ifndef FILE_ACCESS_UNIX_H #define FILE_ACCESS_UNIX_H -#include "core/os/file_access.h" +#include "core/io/file_access.h" #include "core/os/memory.h" #include <stdio.h> @@ -61,21 +61,21 @@ public: virtual String get_path() const; /// returns the path for the current open file virtual String get_path_absolute() const; /// returns the absolute path for the current open file - virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek(uint64_t p_position); ///< seek to a given position virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file - virtual size_t get_position() const; ///< get position in the file - virtual size_t get_len() const; ///< get size of the file + virtual uint64_t get_position() const; ///< get position in the file + virtual uint64_t get_length() const; ///< get size of the file virtual bool eof_reached() const; ///< reading passed EOF virtual uint8_t get_8() const; ///< get a byte - virtual int get_buffer(uint8_t *p_dst, int p_length) const; + virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; virtual Error get_error() const; ///< get last error virtual void flush(); virtual void store_8(uint8_t p_dest); ///< store a byte - virtual void store_buffer(const uint8_t *p_src, int p_length); ///< store an array of bytes + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes virtual bool file_exists(const String &p_path); ///< return true if a file exists diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index 8ec1de4386..d442e521bf 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,24 +36,12 @@ #ifdef WINDOWS_ENABLED #include <stdio.h> -#include <winsock2.h> -// Needs to be included after winsocks2.h +#define WIN32_LEAN_AND_MEAN #include <windows.h> +#include <winsock2.h> #include <ws2tcpip.h> #ifndef UWP_ENABLED -#if defined(__MINGW32__) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 4) -// MinGW-w64 on Ubuntu 12.04 (our Travis build env) has bugs in this code where -// some includes are missing in dependencies of iphlpapi.h for WINVER >= 0x0600 (Vista). -// We don't use this Vista code for now, so working it around by disabling it. -// MinGW-w64 >= 4.0 seems to be better judging by its headers. -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 // Windows XP, disable Vista API -#include <iphlpapi.h> -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 // Re-enable Vista API -#else #include <iphlpapi.h> -#endif // MINGW hack #endif #else // UNIX #include <netdb.h> @@ -75,8 +63,8 @@ #include <net/if.h> // Order is important on OpenBSD, leave as last #endif -static IP_Address _sockaddr2ip(struct sockaddr *p_addr) { - IP_Address ip; +static IPAddress _sockaddr2ip(struct sockaddr *p_addr) { + IPAddress ip; if (p_addr->sa_family == AF_INET) { struct sockaddr_in *addr = (struct sockaddr_in *)p_addr; @@ -84,12 +72,12 @@ static IP_Address _sockaddr2ip(struct sockaddr *p_addr) { } else if (p_addr->sa_family == AF_INET6) { struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; ip.set_ipv6(addr6->sin6_addr.s6_addr); - }; + } return ip; -}; +} -IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) { +void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const { struct addrinfo hints; struct addrinfo *result = nullptr; @@ -102,35 +90,45 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) { } else { hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; - }; + } hints.ai_flags &= ~AI_NUMERICHOST; int s = getaddrinfo(p_hostname.utf8().get_data(), nullptr, &hints, &result); if (s != 0) { ERR_PRINT("getaddrinfo failed! Cannot resolve hostname."); - return IP_Address(); - }; + return; + } if (result == nullptr || result->ai_addr == nullptr) { ERR_PRINT("Invalid response from getaddrinfo"); if (result) { freeaddrinfo(result); } - return IP_Address(); - }; + return; + } - IP_Address ip = _sockaddr2ip(result->ai_addr); + struct addrinfo *next = result; - freeaddrinfo(result); + do { + if (next->ai_addr == nullptr) { + next = next->ai_next; + continue; + } + IPAddress ip = _sockaddr2ip(next->ai_addr); + if (ip.is_valid() && !r_addresses.find(ip)) { + r_addresses.push_back(ip); + } + next = next->ai_next; + } while (next); - return ip; + freeaddrinfo(result); } #if defined(WINDOWS_ENABLED) #if defined(UWP_ENABLED) -void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const { +void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const { using namespace Windows::Networking; using namespace Windows::Networking::Connectivity; @@ -140,8 +138,9 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co for (int i = 0; i < hostnames->Size; i++) { auto hostname = hostnames->GetAt(i); - if (hostname->Type != HostNameType::Ipv4 && hostname->Type != HostNameType::Ipv6) + if (hostname->Type != HostNameType::Ipv4 && hostname->Type != HostNameType::Ipv6) { continue; + } String name = hostname->RawName->Data(); Map<String, Interface_Info>::Element *E = r_interfaces->find(name); @@ -156,14 +155,14 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co Interface_Info &info = E->get(); - IP_Address ip = IP_Address(hostname->CanonicalName->Data()); + IPAddress ip = IPAddress(hostname->CanonicalName->Data()); info.ip_addresses.push_front(ip); } } #else -void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const { +void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const { ULONG buf_size = 1024; IP_ADAPTER_ADDRESSES *addrs; @@ -173,14 +172,14 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co 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; @@ -193,25 +192,27 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress; while (address != nullptr) { int family = address->Address.lpSockaddr->sa_family; - if (family != AF_INET && family != AF_INET6) + 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) + if (info.ip_addresses.size() > 0) { r_interfaces->insert(info.name, info); - }; + } + } memfree(addrs); -}; +} #endif #else // UNIX -void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const { +void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const { struct ifaddrs *ifAddrStruct = nullptr; struct ifaddrs *ifa = nullptr; int family; @@ -249,15 +250,15 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co } #endif -void IP_Unix::make_default() { +void IPUnix::make_default() { _create = _create_unix; } -IP *IP_Unix::_create_unix() { - return memnew(IP_Unix); +IP *IPUnix::_create_unix() { + return memnew(IPUnix); } -IP_Unix::IP_Unix() { +IPUnix::IPUnix() { } #endif diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index ca2ee17f4e..f0ad01d248 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,10 +35,10 @@ #if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) -class IP_Unix : public IP { - GDCLASS(IP_Unix, IP); +class IPUnix : public IP { + GDCLASS(IPUnix, IP); - virtual IP_Address _resolve_hostname(const String &p_hostname, IP::Type p_type) override; + virtual void _resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const override; static IP *_create_unix(); @@ -46,7 +46,7 @@ public: virtual void get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const override; static void make_default(); - IP_Unix(); + IPUnix(); }; #endif diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index 19753943c8..3130d5cae2 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -95,7 +95,7 @@ #endif -size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type) { +size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) { memset(p_addr, 0, sizeof(struct sockaddr_storage)); if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket @@ -106,7 +106,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const addr6->sin6_family = AF_INET6; addr6->sin6_port = htons(p_port); if (p_ip.is_valid()) { - copymem(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16); + memcpy(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16); } else { addr6->sin6_addr = in6addr_any; } @@ -121,7 +121,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const addr4->sin_port = htons(p_port); // short, network byte order if (p_ip.is_valid()) { - copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4); + memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4); } else { addr4->sin_addr.s_addr = INADDR_ANY; } @@ -130,19 +130,24 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const } } -void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port) { +void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) { if (p_addr->ss_family == AF_INET) { struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; - r_ip.set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr)); - - r_port = ntohs(addr4->sin_port); - + if (r_ip) { + r_ip->set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr)); + } + if (r_port) { + *r_port = ntohs(addr4->sin_port); + } } else if (p_addr->ss_family == AF_INET6) { struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; - r_ip.set_ipv6(addr6->sin6_addr.s6_addr); - - r_port = ntohs(addr6->sin6_port); - }; + if (r_ip) { + r_ip->set_ipv6(addr6->sin6_addr.s6_addr); + } + if (r_port) { + *r_port = ntohs(addr6->sin6_port); + } + } } NetSocket *NetSocketPosix::_create_func() { @@ -186,13 +191,21 @@ NetSocketPosix::~NetSocketPosix() { NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { #if defined(WINDOWS_ENABLED) int err = WSAGetLastError(); - - if (err == WSAEISCONN) + if (err == WSAEISCONN) { return ERR_NET_IS_CONNECTED; - if (err == WSAEINPROGRESS || err == WSAEALREADY) + } + if (err == WSAEINPROGRESS || err == WSAEALREADY) { return ERR_NET_IN_PROGRESS; - if (err == WSAEWOULDBLOCK) + } + if (err == WSAEWOULDBLOCK) { return ERR_NET_WOULD_BLOCK; + } + if (err == WSAEADDRINUSE || err == WSAEADDRNOTAVAIL) { + return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE; + } + if (err == WSAEACCES) { + return ERR_NET_UNAUTHORIZED; + } print_verbose("Socket error: " + itos(err)); return ERR_NET_OTHER; #else @@ -205,6 +218,12 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { if (errno == EAGAIN || errno == EWOULDBLOCK) { return ERR_NET_WOULD_BLOCK; } + if (errno == EADDRINUSE || errno == EINVAL || errno == EADDRNOTAVAIL) { + return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE; + } + if (errno == EACCES) { + return ERR_NET_UNAUTHORIZED; + } print_verbose("Socket error: " + itos(errno)); return ERR_NET_OTHER; #endif @@ -214,7 +233,7 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { #pragma GCC diagnostic pop #endif -bool NetSocketPosix::_can_use_ip(const IP_Address &p_ip, const bool p_for_bind) const { +bool NetSocketPosix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const { if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) { return false; } else if (!p_for_bind && !p_ip.is_valid()) { @@ -225,7 +244,7 @@ bool NetSocketPosix::_can_use_ip(const IP_Address &p_ip, const bool p_for_bind) return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type); } -_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, String p_if_name, bool p_add) { +_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER); @@ -235,12 +254,12 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, St int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; int ret = -1; - IP_Address if_ip; + IPAddress if_ip; uint32_t if_v6id = 0; Map<String, IP::Interface_Info> if_info; IP::get_singleton()->get_local_interfaces(&if_info); - for (Map<String, IP::Interface_Info>::Element *E = if_info.front(); E; E = E->next()) { - IP::Interface_Info &c = E->get(); + for (KeyValue<String, IP::Interface_Info> &E : if_info) { + IP::Interface_Info &c = E.value; if (c.name != p_if_name) { continue; } @@ -250,11 +269,11 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, St break; // IPv6 uses index. } - for (List<IP_Address>::Element *F = c.ip_addresses.front(); F; F = F->next()) { - if (!F->get().is_ipv4()) { + for (const IPAddress &F : c.ip_addresses) { + if (!F.is_ipv4()) { continue; // Wrong IP type } - if_ip = F->get(); + if_ip = F; break; } break; @@ -264,13 +283,13 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, St ERR_FAIL_COND_V(!if_ip.is_valid(), ERR_INVALID_PARAMETER); struct ip_mreq greq; int sock_opt = p_add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; - copymem(&greq.imr_multiaddr, p_ip.get_ipv4(), 4); - copymem(&greq.imr_interface, if_ip.get_ipv4(), 4); + memcpy(&greq.imr_multiaddr, p_ip.get_ipv4(), 4); + memcpy(&greq.imr_interface, if_ip.get_ipv4(), 4); ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq)); } else { struct ipv6_mreq greq; int sock_opt = p_add ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP; - copymem(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16); + memcpy(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16); greq.ipv6mr_interface = if_v6id; ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq)); } @@ -306,8 +325,9 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { #if defined(__OpenBSD__) // OpenBSD does not support dual stacking, fallback to IPv4 only. - if (ip_type == IP::TYPE_ANY) + if (ip_type == IP::TYPE_ANY) { ip_type = IP::TYPE_IPV4; + } #endif int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6; @@ -376,7 +396,7 @@ void NetSocketPosix::close() { _is_stream = false; } -Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) { +Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER); @@ -384,8 +404,8 @@ Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) { size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type); if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) { - _get_socket_error(); - print_verbose("Failed to bind socket."); + NetError err = _get_socket_error(); + print_verbose("Failed to bind socket. Error: " + itos(err)); close(); return ERR_UNAVAILABLE; } @@ -401,12 +421,12 @@ Error NetSocketPosix::listen(int p_max_pending) { print_verbose("Failed to listen from socket."); close(); return FAILED; - }; + } return OK; } -Error NetSocketPosix::connect_to_host(IP_Address p_host, uint16_t p_port) { +Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER); @@ -446,8 +466,8 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { FD_ZERO(&wr); FD_ZERO(&ex); FD_SET(_sock, &ex); - struct timeval timeout = { p_timeout, 0 }; - // For blocking operation, pass nullptr timeout pointer to select. + struct timeval timeout = { p_timeout / 1000, (p_timeout % 1000) * 1000 }; + // For blocking operation, pass nullptr timeout pointer to select. struct timeval *tp = nullptr; if (p_timeout >= 0) { // If timeout is non-negative, we want to specify the timeout instead. @@ -475,8 +495,9 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { return FAILED; } - if (ret == 0) + if (ret == 0) { return ERR_BUSY; + } if (FD_ISSET(_sock, &ex)) { _get_socket_error(); @@ -484,10 +505,12 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { return FAILED; } - if (rdp && FD_ISSET(_sock, rdp)) + if (rdp && FD_ISSET(_sock, rdp)) { ready = true; - if (wrp && FD_ISSET(_sock, wrp)) + } + if (wrp && FD_ISSET(_sock, wrp)) { ready = true; + } return ready ? OK : ERR_BUSY; #else @@ -540,7 +563,7 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) { return OK; } -Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port, bool p_peek) { +Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); struct sockaddr_storage from; @@ -597,7 +620,7 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) { return OK; } -Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) { +Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); struct sockaddr_storage addr; @@ -716,7 +739,21 @@ int NetSocketPosix::get_available_bytes() const { return len; } -Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) { +Error NetSocketPosix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const { + ERR_FAIL_COND_V(!is_open(), FAILED); + + struct sockaddr_storage saddr; + socklen_t len = sizeof(saddr); + if (getsockname(_sock, (struct sockaddr *)&saddr, &len) != 0) { + _get_socket_error(); + print_verbose("Error when reading local socket address."); + return FAILED; + } + _set_ip_port(&saddr, r_ip, r_port); + return OK; +} + +Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) { Ref<NetSocket> out; ERR_FAIL_COND_V(!is_open(), out); @@ -729,7 +766,7 @@ Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) { return out; } - _set_ip_port(&their_addr, r_ip, r_port); + _set_ip_port(&their_addr, &r_ip, &r_port); NetSocketPosix *ns = memnew(NetSocketPosix); ns->_set_socket(fd, _ip_type, _is_stream); @@ -737,11 +774,11 @@ Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) { return Ref<NetSocket>(ns); } -Error NetSocketPosix::join_multicast_group(const IP_Address &p_multi_address, String p_if_name) { +Error NetSocketPosix::join_multicast_group(const IPAddress &p_multi_address, String p_if_name) { return _change_multicast_group(p_multi_address, p_if_name, true); } -Error NetSocketPosix::leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) { +Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) { return _change_multicast_group(p_multi_address, p_if_name, false); } #endif diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h index cc6af661c8..867513099a 100644 --- a/drivers/unix/net_socket_posix.h +++ b/drivers/unix/net_socket_posix.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,39 +54,42 @@ private: ERR_NET_WOULD_BLOCK, ERR_NET_IS_CONNECTED, ERR_NET_IN_PROGRESS, - ERR_NET_OTHER + ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE, + ERR_NET_UNAUTHORIZED, + ERR_NET_OTHER, }; NetError _get_socket_error() const; void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream); - _FORCE_INLINE_ Error _change_multicast_group(IP_Address p_ip, String p_if_name, bool p_add); + _FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add); _FORCE_INLINE_ void _set_close_exec_enabled(bool p_enabled); protected: static NetSocket *_create_func(); - bool _can_use_ip(const IP_Address &p_ip, const bool p_for_bind) const; + bool _can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const; public: static void make_default(); static void cleanup(); - static void _set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port); - static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type); + static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port); + static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type); virtual Error open(Type p_sock_type, IP::Type &ip_type); virtual void close(); - virtual Error bind(IP_Address p_addr, uint16_t p_port); + virtual Error bind(IPAddress p_addr, uint16_t p_port); virtual Error listen(int p_max_pending); - virtual Error connect_to_host(IP_Address p_host, uint16_t p_port); + virtual Error connect_to_host(IPAddress p_host, uint16_t p_port); virtual Error poll(PollType p_type, int timeout) const; virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read); - virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port, bool p_peek = false); + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false); virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent); - virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port); - virtual Ref<NetSocket> accept(IP_Address &r_ip, uint16_t &r_port); + virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port); + virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port); virtual bool is_open() const; virtual int get_available_bytes() const; + virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const; virtual Error set_broadcasting_enabled(bool p_enabled); virtual void set_blocking_enabled(bool p_enabled); @@ -94,8 +97,8 @@ public: virtual void set_tcp_no_delay_enabled(bool p_enabled); virtual void set_reuse_address_enabled(bool p_enabled); virtual void set_reuse_port_enabled(bool p_enabled); - virtual Error join_multicast_group(const IP_Address &p_multi_address, String p_if_name); - virtual Error leave_multicast_group(const IP_Address &p_multi_address, String p_if_name); + virtual Error join_multicast_group(const IPAddress &p_multi_address, String p_if_name); + virtual Error leave_multicast_group(const IPAddress &p_multi_address, String p_if_name); NetSocketPosix(); ~NetSocketPosix(); diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index b9bd773c2e..3b5e1bf91d 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,6 +65,21 @@ #include <time.h> #include <unistd.h> +#if defined(OSX_ENABLED) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) +// Random location for getentropy. Fitting. +#include <sys/random.h> +#define UNIX_GET_ENTROPY +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || (defined(__GLIBC_MINOR__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26)) +// In <unistd.h>. +// One day... (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) +// https://publications.opengroup.org/standards/unix/c211 +#define UNIX_GET_ENTROPY +#endif + +#if !defined(UNIX_GET_ENTROPY) && !defined(NO_URANDOM) +#include <fcntl.h> +#endif + /// Clock Setup function (used by get_ticks_usec) static uint64_t _clock_start = 0; #if defined(__APPLE__) @@ -91,7 +106,7 @@ static void _setup_clock() { void OS_Unix::debug_break() { assert(false); -}; +} static void handle_interrupt(int sig) { if (!EngineDebugger::is_active()) { @@ -129,7 +144,7 @@ void OS_Unix::initialize_core() { #ifndef NO_NETWORK NetSocketPosix::make_default(); - IP_Unix::make_default(); + IPUnix::make_default(); #endif _setup_clock(); @@ -139,10 +154,6 @@ void OS_Unix::finalize_core() { NetSocketPosix::cleanup(); } -void OS_Unix::alert(const String &p_alert, const String &p_title) { - fprintf(stderr, "ERROR: %s\n", p_alert.utf8().get_data()); -} - String OS_Unix::get_stdin_string(bool p_block) { if (p_block) { char buff[1024]; @@ -154,6 +165,31 @@ String OS_Unix::get_stdin_string(bool p_block) { return ""; } +Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) { +#if defined(UNIX_GET_ENTROPY) + int left = p_bytes; + int ofs = 0; + do { + int chunk = MIN(left, 256); + ERR_FAIL_COND_V(getentropy(r_buffer + ofs, chunk), FAILED); + left -= chunk; + ofs += chunk; + } while (left > 0); +#elif !defined(NO_URANDOM) + int r = open("/dev/urandom", O_RDONLY); + ERR_FAIL_COND_V(r < 0, FAILED); + int left = p_bytes; + do { + ssize_t ret = read(r, r_buffer, p_bytes); + ERR_FAIL_COND_V(ret <= 0, FAILED); + left -= ret; + } while (left > 0); +#else + return ERR_UNAVAILABLE; +#endif + return OK; +} + String OS_Unix::get_name() const { return "Unix"; } @@ -162,12 +198,12 @@ double OS_Unix::get_unix_time() const { struct timeval tv_now; gettimeofday(&tv_now, nullptr); return (double)tv_now.tv_sec + double(tv_now.tv_usec) / 1000000; -}; +} -OS::Date OS_Unix::get_date(bool utc) const { +OS::Date OS_Unix::get_date(bool p_utc) const { time_t t = time(nullptr); struct tm lt; - if (utc) { + if (p_utc) { gmtime_r(&t, <); } else { localtime_r(&t, <); @@ -185,18 +221,18 @@ OS::Date OS_Unix::get_date(bool utc) const { return ret; } -OS::Time OS_Unix::get_time(bool utc) const { +OS::Time OS_Unix::get_time(bool p_utc) const { time_t t = time(nullptr); struct tm lt; - if (utc) { + if (p_utc) { gmtime_r(&t, <); } else { localtime_r(&t, <); } Time ret; ret.hour = lt.tm_hour; - ret.min = lt.tm_min; - ret.sec = lt.tm_sec; + ret.minute = lt.tm_min; + ret.second = lt.tm_sec; get_time_zone_info(); return ret; } @@ -253,7 +289,7 @@ uint64_t OS_Unix::get_ticks_usec() const { return longtime; } -Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { +Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { #ifdef __EMSCRIPTEN__ // Don't compile this code at all to avoid undefined references. // Actual virtual call goes to OS_JavaScript. @@ -322,7 +358,7 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, St #endif } -Error OS_Unix::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) { +Error OS_Unix::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) { #ifdef __EMSCRIPTEN__ // Don't compile this code at all to avoid undefined references. // Actual virtual call goes to OS_JavaScript. @@ -374,7 +410,7 @@ Error OS_Unix::kill(const ProcessID &p_pid) { int OS_Unix::get_process_id() const { return getpid(); -}; +} bool OS_Unix::has_environment(const String &p_var) const { return getenv(p_var.utf8().get_data()) != nullptr; @@ -396,19 +432,19 @@ String OS_Unix::get_locale() const { Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { String path = p_path; - if (FileAccess::exists(path) && path.is_rel_path()) { + if (FileAccess::exists(path) && path.is_relative_path()) { // dlopen expects a slash, in this case a leading ./ for it to be interpreted as a relative path, // otherwise it will end up searching various system directories for the lib instead and finally failing. path = "./" + path; } if (!FileAccess::exists(path)) { - //this code exists so gdnative can load .so files from within the executable path + // This code exists so GDExtension can load .so files from within the executable path. path = get_executable_path().get_base_dir().plus_file(p_path.get_file()); } if (!FileAccess::exists(path)) { - //this code exists so gdnative can load .so files from a standard unix location + // This code exists so GDExtension can load .so files from a standard unix location. path = get_executable_path().get_base_dir().plus_file("../lib").plus_file(p_path.get_file()); } @@ -464,11 +500,11 @@ int OS_Unix::get_processor_count() const { String OS_Unix::get_user_data_dir() const { String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); - if (appname != "") { + if (!appname.is_empty()) { bool use_custom_dir = ProjectSettings::get_singleton()->get("application/config/use_custom_user_dir"); if (use_custom_dir) { String custom_dir = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/custom_user_dir_name"), true); - if (custom_dir == "") { + if (custom_dir.is_empty()) { custom_dir = appname; } return get_data_path().plus_file(custom_dir); @@ -477,7 +513,7 @@ String OS_Unix::get_user_data_dir() const { } } - return ProjectSettings::get_singleton()->get_resource_path(); + return get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file("[unnamed project]"); } String OS_Unix::get_executable_path() const { @@ -490,7 +526,7 @@ String OS_Unix::get_executable_path() const { if (len > 0) { b.parse_utf8(buf, len); } - if (b == "") { + if (b.is_empty()) { WARN_PRINT("Couldn't get executable path from /proc/self/exe, using argv[0]"); return OS::get_executable_path(); } @@ -519,8 +555,9 @@ String OS_Unix::get_executable_path() const { char *resolved_path = new char[buff_size + 1]; - if (_NSGetExecutablePath(resolved_path, &buff_size) == 1) + if (_NSGetExecutablePath(resolved_path, &buff_size) == 1) { WARN_PRINT("MAXPATHLEN is too small"); + } String path(resolved_path); delete[] resolved_path; @@ -532,7 +569,7 @@ String OS_Unix::get_executable_path() const { #endif } -void UnixTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { +void UnixTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) { if (!should_log(true)) { return; } diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index 6c79d984e9..460ba4b9e1 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,7 +43,6 @@ protected: virtual void initialize_core(); virtual int unix_initialize_audio(int p_audio_driver); - //virtual Error initialize(int p_video_driver,int p_audio_driver); virtual void finalize_core() override; @@ -52,18 +51,9 @@ protected: public: OS_Unix(); - virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual String get_stdin_string(bool p_block) override; - //virtual void set_mouse_show(bool p_show); - //virtual void set_mouse_grab(bool p_grab); - //virtual bool is_mouse_grab_enabled() const = 0; - //virtual void get_mouse_position(int &x, int &y) const; - //virtual void set_window_title(const String& p_title); - - //virtual void set_video_mode(const VideoMode& p_video_mode); - //virtual VideoMode get_video_mode() const; - //virtual void get_fullscreen_mode_list(List<VideoMode> *p_list) const; + virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; // Should return cryptographycally-safe random bytes. virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override; virtual Error close_dynamic_library(void *p_library_handle) override; @@ -73,8 +63,8 @@ public: virtual String get_name() const override; - virtual Date get_date(bool utc) const override; - virtual Time get_time(bool utc) const override; + virtual Date get_date(bool p_utc) const override; + virtual Time get_time(bool p_utc) const override; virtual TimeZoneInfo get_time_zone_info() const override; virtual double get_unix_time() const override; @@ -82,8 +72,8 @@ public: virtual void delay_usec(uint32_t p_usec) const override; virtual uint64_t get_ticks_usec() const override; - virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override; - virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override; + virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override; + virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override; virtual Error kill(const ProcessID &p_pid) override; virtual int get_process_id() const override; @@ -103,7 +93,7 @@ public: class UnixTerminalLogger : public StdLogger { public: - virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); + virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR) override; virtual ~UnixTerminalLogger(); }; diff --git a/drivers/unix/syslog_logger.cpp b/drivers/unix/syslog_logger.cpp index 423ddac793..6189d645c6 100644 --- a/drivers/unix/syslog_logger.cpp +++ b/drivers/unix/syslog_logger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,9 @@ #ifdef UNIX_ENABLED #include "syslog_logger.h" + #include "core/string/print_string.h" + #include <syslog.h> void SyslogLogger::logv(const char *p_format, va_list p_list, bool p_err) { diff --git a/drivers/unix/syslog_logger.h b/drivers/unix/syslog_logger.h index d9f7f2ff99..697a96a6f9 100644 --- a/drivers/unix/syslog_logger.h +++ b/drivers/unix/syslog_logger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/unix/thread_posix.cpp b/drivers/unix/thread_posix.cpp index 19fab1d475..cb5f261e6e 100644 --- a/drivers/unix/thread_posix.cpp +++ b/drivers/unix/thread_posix.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,6 +35,10 @@ #include "core/os/thread.h" #include "core/string/ustring.h" +#ifdef PTHREAD_BSD_SET_NAME +#include <pthread_np.h> +#endif + static Error set_name(const String &p_name) { #ifdef PTHREAD_NO_RENAME return ERR_UNAVAILABLE; diff --git a/drivers/unix/thread_posix.h b/drivers/unix/thread_posix.h index 8b8a736bf0..9cd3ecbe90 100644 --- a/drivers/unix/thread_posix.h +++ b/drivers/unix/thread_posix.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/vulkan/SCsub b/drivers/vulkan/SCsub index 14b9d63204..b6ceb1cdea 100644 --- a/drivers/vulkan/SCsub +++ b/drivers/vulkan/SCsub @@ -3,116 +3,48 @@ Import("env") thirdparty_obj = [] +thirdparty_dir = "#thirdparty/vulkan" +thirdparty_volk_dir = "#thirdparty/volk" -# FIXME: Refactor all this to reduce code duplication. -if env["platform"] == "android": - # Use NDK Vulkan headers - thirdparty_dir = env["ANDROID_NDK_ROOT"] + "/sources/third_party/vulkan/src" - thirdparty_includes = [ - thirdparty_dir, - thirdparty_dir + "/include", - thirdparty_dir + "/layers", - thirdparty_dir + "/layers/generated", - ] - env.Prepend(CPPPATH=thirdparty_includes) - - # Build Vulkan memory allocator - env_thirdparty = env.Clone() - env_thirdparty.disable_warnings() - - thirdparty_dir = "#thirdparty/vulkan" - vma_sources = [thirdparty_dir + "/android/vk_mem_alloc.cpp"] - env_thirdparty.add_source_files(thirdparty_obj, vma_sources) +# Use bundled Vulkan headers +env.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"]) + +if env["use_volk"]: + env.AppendUnique(CPPDEFINES=["USE_VOLK"]) + env.Prepend(CPPPATH=[thirdparty_volk_dir]) +if env["platform"] == "android": + env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_ANDROID_KHR"]) elif env["platform"] == "iphone": - # Use bundled Vulkan headers - thirdparty_dir = "#thirdparty/vulkan" - env.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include", thirdparty_dir + "/loader"]) - - # Build Vulkan memory allocator - env_thirdparty = env.Clone() - env_thirdparty.disable_warnings() - - vma_sources = [thirdparty_dir + "/vk_mem_alloc.cpp"] - env_thirdparty.add_source_files(thirdparty_obj, vma_sources) - -elif env["builtin_vulkan"]: - # Use bundled Vulkan headers - thirdparty_dir = "#thirdparty/vulkan" - env.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include", thirdparty_dir + "/loader"]) - - # Build Vulkan loader library - env_thirdparty = env.Clone() - env_thirdparty.disable_warnings() - - loader_sources = [ - "cJSON.c", - "debug_utils.c", - "dev_ext_trampoline.c", - "loader.c", - "murmurhash.c", - "phys_dev_ext.c", - "trampoline.c", - "unknown_ext_chain.c", - "wsi.c", - "extension_manual.c", - ] - vma_sources = [thirdparty_dir + "/vk_mem_alloc.cpp"] - - if env["platform"] == "windows": - loader_sources.append("dirent_on_windows.c") - env_thirdparty.AppendUnique( - CPPDEFINES=[ - "VK_USE_PLATFORM_WIN32_KHR", - "VULKAN_NON_CMAKE_BUILD", - "WIN32_LEAN_AND_MEAN", - 'API_NAME=\\"%s\\"' % "Vulkan", - ] - ) - if not env.msvc: # Windows 7+, missing in mingw headers - env_thirdparty.AppendUnique( - CPPDEFINES=["CM_GETIDLIST_FILTER_CLASS=0x00000200", "CM_GETIDLIST_FILTER_PRESENT=0x00000100"] - ) - elif env["platform"] == "osx": - env_thirdparty.AppendUnique( - CPPDEFINES=[ - "VK_USE_PLATFORM_MACOS_MVK", - "VULKAN_NON_CMAKE_BUILD", - 'SYSCONFDIR=\\"%s\\"' % "/etc", - 'FALLBACK_DATA_DIRS=\\"%s\\"' % "/usr/local/share:/usr/share", - 'FALLBACK_CONFIG_DIRS=\\"%s\\"' % "/etc/xdg", - ] - ) - elif env["platform"] == "linuxbsd": - env_thirdparty.AppendUnique( - CPPDEFINES=[ - "VK_USE_PLATFORM_XLIB_KHR", - "VULKAN_NON_CMAKE_BUILD", - 'SYSCONFDIR=\\"%s\\"' % "/etc", - 'FALLBACK_DATA_DIRS=\\"%s\\"' % "/usr/local/share:/usr/share", - 'FALLBACK_CONFIG_DIRS=\\"%s\\"' % "/etc/xdg", - ] - ) - import platform - - if platform.system() == "Linux": - # In glibc since 2.17 and musl libc since 1.1.24. Used by loader.c. - env_thirdparty.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"]) - - loader_sources = [thirdparty_dir + "/loader/" + file for file in loader_sources] - env_thirdparty.add_source_files(thirdparty_obj, loader_sources) - env_thirdparty.add_source_files(thirdparty_obj, vma_sources) - -else: # Always build VMA. - thirdparty_dir = "#thirdparty/vulkan" - env.Prepend(CPPPATH=[thirdparty_dir]) - - # Build Vulkan loader library - env_thirdparty = env.Clone() - env_thirdparty.disable_warnings() - vma_sources = [thirdparty_dir + "/vk_mem_alloc.cpp"] - - env_thirdparty.add_source_files(thirdparty_obj, vma_sources) + env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_IOS_MVK"]) +elif env["platform"] == "linuxbsd": + env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_XLIB_KHR"]) +elif env["platform"] == "osx": + env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_MACOS_MVK"]) +elif env["platform"] == "windows": + env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_WIN32_KHR"]) + +# Build Vulkan memory allocator and volk +env_thirdparty_vma = env.Clone() +env_thirdparty_vma.disable_warnings() +thirdparty_sources_vma = [thirdparty_dir + "/vk_mem_alloc.cpp"] + +if env["use_volk"]: + env_thirdparty_vma.AppendUnique(CPPDEFINES=["VMA_STATIC_VULKAN_FUNCTIONS=1"]) + env_thirdparty_volk = env.Clone() + env_thirdparty_volk.disable_warnings() + + thirdparty_sources_volk = [thirdparty_volk_dir + "/volk.c"] + env_thirdparty_volk.add_source_files(thirdparty_obj, thirdparty_sources_volk) +elif env["platform"] == "android": + # Our current NDK version only provides old Vulkan headers, + # so we have to limit VMA. + env_thirdparty_vma.AppendUnique(CPPDEFINES=["VMA_VULKAN_VERSION=1000000"]) +elif env["platform"] == "osx" or env["platform"] == "iphone": + # MoltenVK supports only Vulkan 1.1 API, limit VMA to the same version. + env_thirdparty_vma.AppendUnique(CPPDEFINES=["VMA_VULKAN_VERSION=1001000"]) + +env_thirdparty_vma.add_source_files(thirdparty_obj, thirdparty_sources_vma) env.drivers_sources += thirdparty_obj diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 9584dd3f67..62de01e8bb 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,20 +31,25 @@ #include "rendering_device_vulkan.h" #include "core/config/project_settings.h" -#include "core/os/file_access.h" +#include "core/io/compression.h" +#include "core/io/file_access.h" +#include "core/io/marshalls.h" #include "core/os/os.h" #include "core/templates/hashfuncs.h" #include "drivers/vulkan/vulkan_context.h" +#include "thirdparty/misc/smolv.h" #include "thirdparty/spirv-reflect/spirv_reflect.h" //#define FORCE_FULL_BARRIER +static const uint32_t SMALL_ALLOCATION_MAX_SIZE = 4096; + // Get the Vulkan object information and possible stage access types (bitwise OR'd with incoming values) RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &r_stage_mask, VkAccessFlags &r_access_mask, uint32_t p_post_barrier) { Buffer *buffer = nullptr; if (vertex_buffer_owner.owns(p_buffer)) { - buffer = vertex_buffer_owner.getornull(p_buffer); + buffer = vertex_buffer_owner.get_or_null(p_buffer); r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; r_access_mask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; @@ -61,7 +66,7 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID } else if (index_buffer_owner.owns(p_buffer)) { r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; r_access_mask |= VK_ACCESS_INDEX_READ_BIT; - buffer = index_buffer_owner.getornull(p_buffer); + buffer = index_buffer_owner.get_or_null(p_buffer); } else if (uniform_buffer_owner.owns(p_buffer)) { if (p_post_barrier & BARRIER_MASK_RASTER) { r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; @@ -70,7 +75,7 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID r_stage_mask |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; } r_access_mask |= VK_ACCESS_UNIFORM_READ_BIT; - buffer = uniform_buffer_owner.getornull(p_buffer); + buffer = uniform_buffer_owner.get_or_null(p_buffer); } else if (texture_buffer_owner.owns(p_buffer)) { if (p_post_barrier & BARRIER_MASK_RASTER) { r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; @@ -81,9 +86,9 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID r_access_mask |= VK_ACCESS_SHADER_READ_BIT; } - buffer = &texture_buffer_owner.getornull(p_buffer)->buffer; + buffer = &texture_buffer_owner.get_or_null(p_buffer)->buffer; } else if (storage_buffer_owner.owns(p_buffer)) { - buffer = storage_buffer_owner.getornull(p_buffer); + buffer = storage_buffer_owner.get_or_null(p_buffer); if (p_post_barrier & BARRIER_MASK_RASTER) { r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; @@ -119,7 +124,7 @@ static void update_external_dependency_for_store(VkSubpassDependency &dependency } if (is_depth) { - // Depth resources have addtional stages that may be interested in them + // Depth resources have additional stages that may be interested in them dependency.dstStageMask |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; dependency.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } @@ -384,14 +389,6 @@ const VkFormat RenderingDeviceVulkan::vulkan_formats[RenderingDevice::DATA_FORMA VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, - VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, - VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, - VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, - VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, - VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, - VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, - VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, - VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG, }; const char *RenderingDeviceVulkan::named_formats[RenderingDevice::DATA_FORMAT_MAX] = { @@ -613,14 +610,6 @@ const char *RenderingDeviceVulkan::named_formats[RenderingDevice::DATA_FORMAT_MA "G16_B16_R16_3Plane_422_Unorm", "G16_B16R16_2Plane_422_Unorm", "G16_B16_R16_3Plane_444_Unorm", - "Pvrtc1_2Bpp_Unorm_Block_Img", - "Pvrtc1_4Bpp_Unorm_Block_Img", - "Pvrtc2_2Bpp_Unorm_Block_Img", - "Pvrtc2_4Bpp_Unorm_Block_Img", - "Pvrtc1_2Bpp_Srgb_Block_Img", - "Pvrtc1_4Bpp_Srgb_Block_Img", - "Pvrtc2_2Bpp_Srgb_Block_Img", - "Pvrtc2_4Bpp_Srgb_Block_Img" }; int RenderingDeviceVulkan::get_format_vertex_size(DataFormat p_format) { @@ -967,15 +956,6 @@ uint32_t RenderingDeviceVulkan::get_image_format_pixel_size(DataFormat p_format) case DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM: case DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM: return 8; - case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: - return 1; default: { ERR_PRINT("Format not handled, bug"); } @@ -1045,20 +1025,6 @@ void RenderingDeviceVulkan::get_compressed_image_format_block_dimensions(DataFor r_w = 4; r_h = 4; return; - case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: - r_w = 4; - r_h = 4; - return; - case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: - r_w = 8; - r_h = 4; - return; default: { r_w = 1; r_h = 1; @@ -1135,15 +1101,6 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: return 8; //wrong - case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: - return 8; //what varies is resolution default: { } } @@ -1164,16 +1121,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_pixel_rshift(DataFor case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: case DATA_FORMAT_EAC_R11_UNORM_BLOCK: case DATA_FORMAT_EAC_R11_SNORM_BLOCK: - case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: return 1; - case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: //these formats are quarter byte size, so rshift is 1 - case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: - case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: - case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: - return 2; default: { } } @@ -1228,7 +1176,7 @@ uint32_t RenderingDeviceVulkan::get_image_format_required_size(DataFormat p_form } w = MAX(blockw, w >> 1); h = MAX(blockh, h >> 1); - d = MAX(1, d >> 1); + d = MAX(1u, d >> 1); } return size; @@ -1236,23 +1184,23 @@ uint32_t RenderingDeviceVulkan::get_image_format_required_size(DataFormat p_form uint32_t RenderingDeviceVulkan::get_image_required_mipmaps(uint32_t p_width, uint32_t p_height, uint32_t p_depth) { //formats and block size don't really matter here since they can all go down to 1px (even if block is larger) - int w = p_width; - int h = p_height; - int d = p_depth; + uint32_t w = p_width; + uint32_t h = p_height; + uint32_t d = p_depth; - int mipmaps = 1; + uint32_t mipmaps = 1; while (true) { if (w == 1 && h == 1 && d == 1) { break; } - w = MAX(1, w >> 1); - h = MAX(1, h >> 1); - d = MAX(1, d >> 1); + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); + d = MAX(1u, d >> 1); mipmaps++; - }; + } return mipmaps; } @@ -1389,6 +1337,11 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, allocInfo.memoryTypeBits = 0; allocInfo.pool = nullptr; allocInfo.pUserData = nullptr; + if (p_size <= SMALL_ALLOCATION_MAX_SIZE) { + uint32_t mem_type_index = 0; + vmaFindMemoryTypeIndexForBufferInfo(allocator, &bufferInfo, &allocInfo, &mem_type_index); + allocInfo.pool = _find_or_create_small_allocs_pool(mem_type_index); + } VkResult err = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &p_buffer->buffer, &p_buffer->allocation, nullptr); ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + "."); @@ -1398,12 +1351,15 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, p_buffer->buffer_info.range = p_size; p_buffer->usage = p_usage; + buffer_memory += p_size; + return OK; } Error RenderingDeviceVulkan::_buffer_free(Buffer *p_buffer) { ERR_FAIL_COND_V(p_buffer->size == 0, ERR_INVALID_PARAMETER); + buffer_memory -= p_buffer->size; vmaDestroyBuffer(allocator, p_buffer->buffer, p_buffer->allocation); p_buffer->buffer = VK_NULL_HANDLE; p_buffer->allocation = nullptr; @@ -1486,7 +1442,7 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_ // possible in a single frame if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { //guess we did.. ok, let's see if we can insert a new block.. - if (staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { //we can, so we are safe Error err = _insert_staging_block(); if (err) { @@ -1530,7 +1486,7 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_ staging_buffer_blocks.write[staging_buffer_current].fill_amount = 0; } else if (staging_buffer_blocks[staging_buffer_current].frame_used > frames_drawn - frame_count) { //this block may still be in use, let's not touch it unless we have to, so.. can we create a new one? - if (staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { //we are still allowed to create a new block, so let's do that and insert it for current pos Error err = _insert_staging_block(); if (err) { @@ -1600,7 +1556,7 @@ Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, c } //copy to staging buffer - copymem(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount); + memcpy(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount); //unmap vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation); @@ -1798,6 +1754,10 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_create_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } + if (p_format.usage_bits & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + } + if (p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT) { image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } @@ -1883,20 +1843,28 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T //allocate memory + uint32_t width, height; + uint32_t image_size = get_image_format_required_size(p_format.format, p_format.width, p_format.height, p_format.depth, p_format.mipmaps, &width, &height); + VmaAllocationCreateInfo allocInfo; allocInfo.flags = 0; + allocInfo.pool = nullptr; allocInfo.usage = p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT ? VMA_MEMORY_USAGE_CPU_ONLY : VMA_MEMORY_USAGE_GPU_ONLY; allocInfo.requiredFlags = 0; allocInfo.preferredFlags = 0; allocInfo.memoryTypeBits = 0; - allocInfo.pool = nullptr; allocInfo.pUserData = nullptr; + if (image_size <= SMALL_ALLOCATION_MAX_SIZE) { + uint32_t mem_type_index = 0; + vmaFindMemoryTypeIndexForImageInfo(allocator, &image_create_info, &allocInfo, &mem_type_index); + allocInfo.pool = _find_or_create_small_allocs_pool(mem_type_index); + } Texture texture; VkResult err = vmaCreateImage(allocator, &image_create_info, &allocInfo, &texture.image, &texture.allocation, &texture.allocation_info); ERR_FAIL_COND_V_MSG(err, RID(), "vmaCreateImage failed with error " + itos(err) + "."); - + image_memory += texture.allocation_info.size; texture.type = p_format.texture_type; texture.format = p_format.format; texture.width = image_create_info.extent.width; @@ -2029,7 +1997,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T if (p_data.size()) { for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) { - texture_update(id, i, p_data[i]); + _texture_update(id, i, p_data[i], RD::BARRIER_MASK_ALL, true); } } return id; @@ -2038,12 +2006,12 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID p_with_texture) { _THREAD_SAFE_METHOD_ - Texture *src_texture = texture_owner.getornull(p_with_texture); + Texture *src_texture = texture_owner.get_or_null(p_with_texture); ERR_FAIL_COND_V(!src_texture, RID()); if (src_texture->owner.is_valid()) { //ahh this is a share p_with_texture = src_texture->owner; - src_texture = texture_owner.getornull(src_texture->owner); + src_texture = texture_owner.get_or_null(src_texture->owner); ERR_FAIL_COND_V(!src_texture, RID()); //this is a bug } @@ -2128,6 +2096,10 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID } } + if (texture.usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) { + usage_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + } + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { usage_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } @@ -2156,15 +2128,133 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID return id; } -RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, TextureSliceType p_slice_type) { +RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, uint64_t p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers) { + _THREAD_SAFE_METHOD_ + // This method creates a texture object using a VkImage created by an extension, module or other external source (OpenXR uses this). + VkImage image = (VkImage)p_image; + + Texture texture; + texture.image = image; + // if we leave texture.allocation as a nullptr, would that be enough to detect we don't "own" the image? + // also leave texture.allocation_info alone + // we'll set texture.view later on + texture.type = p_type; + texture.format = p_format; + texture.samples = p_samples; + texture.width = p_width; + texture.height = p_height; + texture.depth = p_depth; + texture.layers = p_layers; + texture.mipmaps = 0; // maybe make this settable too? + texture.usage_flags = p_flags; + texture.base_mipmap = 0; + texture.base_layer = 0; + texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM); + texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB); + + // Do we need to do something with texture.layout ? + + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + texture.read_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + + // if (format_has_stencil(p_format.format)) { + // texture.barrier_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + // } + } else { + texture.read_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + // Create a view for us to use + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = nullptr; + image_view_create_info.flags = 0; + image_view_create_info.image = texture.image; + + static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { + VK_IMAGE_VIEW_TYPE_1D, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_VIEW_TYPE_3D, + VK_IMAGE_VIEW_TYPE_CUBE, + VK_IMAGE_VIEW_TYPE_1D_ARRAY, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, + }; + + image_view_create_info.viewType = view_types[texture.type]; + image_view_create_info.format = vulkan_formats[texture.format]; + + static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_ONE, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + + // hardcode for now, maybe make this settable from outside.. + image_view_create_info.components.r = component_swizzles[TEXTURE_SWIZZLE_R]; + image_view_create_info.components.g = component_swizzles[TEXTURE_SWIZZLE_G]; + image_view_create_info.components.b = component_swizzles[TEXTURE_SWIZZLE_B]; + image_view_create_info.components.a = component_swizzles[TEXTURE_SWIZZLE_A]; + + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = texture.mipmaps; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + image_view_create_info.subresourceRange.layerCount = texture.layers; + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } else { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + VkResult err = vkCreateImageView(device, &image_view_create_info, nullptr, &texture.view); + + if (err) { + // vmaDestroyImage(allocator, texture.image, texture.allocation); + ERR_FAIL_V_MSG(RID(), "vkCreateImageView failed with error " + itos(err) + "."); + } + + //barrier to set layout + { + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = nullptr; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_memory_barrier.newLayout = texture.layout; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = texture.image; + image_memory_barrier.subresourceRange.aspectMask = texture.barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = texture.mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = texture.layers; + + vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + } + + RID id = texture_owner.make_rid(texture); + + return id; +} + +RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps, TextureSliceType p_slice_type) { _THREAD_SAFE_METHOD_ - Texture *src_texture = texture_owner.getornull(p_with_texture); + Texture *src_texture = texture_owner.get_or_null(p_with_texture); ERR_FAIL_COND_V(!src_texture, RID()); if (src_texture->owner.is_valid()) { //ahh this is a share p_with_texture = src_texture->owner; - src_texture = texture_owner.getornull(src_texture->owner); + src_texture = texture_owner.get_or_null(src_texture->owner); ERR_FAIL_COND_V(!src_texture, RID()); //this is a bug } @@ -2180,6 +2270,7 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p //create view ERR_FAIL_UNSIGNED_INDEX_V(p_mipmap, src_texture->mipmaps, RID()); + ERR_FAIL_COND_V(p_mipmap + p_mipmaps > src_texture->mipmaps, RID()); ERR_FAIL_UNSIGNED_INDEX_V(p_layer, src_texture->layers, RID()); int slice_layers = 1; @@ -2192,7 +2283,7 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p Texture texture = *src_texture; get_image_format_required_size(texture.format, texture.width, texture.height, texture.depth, p_mipmap + 1, &texture.width, &texture.height); - texture.mipmaps = 1; + texture.mipmaps = p_mipmaps; texture.layers = slice_layers; texture.base_mipmap = p_mipmap; texture.base_layer = p_layer; @@ -2255,7 +2346,7 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p "Specified layer must be a multiple of 6."); } image_view_create_info.subresourceRange.baseMipLevel = p_mipmap; - image_view_create_info.subresourceRange.levelCount = 1; + image_view_create_info.subresourceRange.levelCount = p_mipmaps; image_view_create_info.subresourceRange.layerCount = slice_layers; image_view_create_info.subresourceRange.baseArrayLayer = p_layer; @@ -2276,17 +2367,21 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p } Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier) { + return _texture_update(p_texture, p_layer, p_data, p_post_barrier, false); +} + +Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier, bool p_use_setup_queue) { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(draw_list || compute_list, ERR_INVALID_PARAMETER, - "Updating textures in is forbidden during creation of a draw or compute list"); + ERR_FAIL_COND_V_MSG((draw_list || compute_list) && !p_use_setup_queue, ERR_INVALID_PARAMETER, + "Updating textures is forbidden during creation of a draw or compute list"); - Texture *texture = texture_owner.getornull(p_texture); + Texture *texture = texture_owner.get_or_null(p_texture); ERR_FAIL_COND_V(!texture, ERR_INVALID_PARAMETER); if (texture->owner != RID()) { p_texture = texture->owner; - texture = texture_owner.getornull(texture->owner); + texture = texture_owner.get_or_null(texture->owner); ERR_FAIL_COND_V(!texture, ERR_BUG); //this is a bug } @@ -2320,7 +2415,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con const uint8_t *r = p_data.ptr(); - VkCommandBuffer command_buffer = p_post_barrier ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer; + VkCommandBuffer command_buffer = p_use_setup_queue ? frames[frame].setup_command_buffer : frames[frame].draw_command_buffer; //barrier to transfer { @@ -2373,7 +2468,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con to_allocate >>= get_compressed_image_format_pixel_rshift(texture->format); uint32_t alloc_offset, alloc_size; - Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false, p_post_barrier); + Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false, !p_use_setup_queue); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); uint8_t *write_ptr; @@ -2461,8 +2556,8 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con } mipmap_offset = image_total; - logic_width = MAX(1, logic_width >> 1); - logic_height = MAX(1, logic_height >> 1); + logic_width = MAX(1u, logic_width >> 1); + logic_height = MAX(1u, logic_height >> 1); } //barrier to restore layout @@ -2502,7 +2597,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } if (texture->used_in_frame != frames_drawn) { @@ -2558,7 +2653,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch; uint8_t *wptr = write_ptr + y * line_width; - copymem(wptr, rptr, line_width); + memcpy(wptr, rptr, line_width); } } else { @@ -2566,7 +2661,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex for (uint32_t y = 0; y < height; y++) { const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch; uint8_t *wptr = write_ptr + y * pixel_size * width; - copymem(wptr, rptr, pixel_size * width); + memcpy(wptr, rptr, (uint64_t)pixel_size * width); } } } @@ -2583,7 +2678,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t p_layer) { _THREAD_SAFE_METHOD_ - Texture *tex = texture_owner.getornull(p_texture); + Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND_V(!tex, Vector<uint8_t>()); ERR_FAIL_COND_V_MSG(tex->bound, Vector<uint8_t>(), @@ -2660,9 +2755,9 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t vkCmdCopyImageToBuffer(command_buffer, tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, tmp_buffer.buffer, 1, &buffer_image_copy); - computed_w = MAX(1, computed_w >> 1); - computed_h = MAX(1, computed_h >> 1); - computed_d = MAX(1, computed_d >> 1); + computed_w = MAX(1u, computed_w >> 1); + computed_h = MAX(1u, computed_h >> 1); + computed_d = MAX(1u, computed_d >> 1); offset += size; } @@ -2699,7 +2794,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t { buffer_data.resize(buffer_size); uint8_t *w = buffer_data.ptrw(); - copymem(w, buffer_mem, buffer_size); + memcpy(w, buffer_mem, buffer_size); } vmaUnmapMemory(allocator, tmp_buffer.allocation); @@ -2713,7 +2808,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t bool RenderingDeviceVulkan::texture_is_shared(RID p_texture) { _THREAD_SAFE_METHOD_ - Texture *tex = texture_owner.getornull(p_texture); + Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND_V(!tex, false); return tex->owner.is_valid(); } @@ -2722,10 +2817,18 @@ bool RenderingDeviceVulkan::texture_is_valid(RID p_texture) { return texture_owner.owns(p_texture); } +Size2i RenderingDeviceVulkan::texture_size(RID p_texture) { + _THREAD_SAFE_METHOD_ + + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!tex, Size2i()); + return Size2i(tex->width, tex->height); +} + Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, uint32_t p_post_barrier) { _THREAD_SAFE_METHOD_ - Texture *src_tex = texture_owner.getornull(p_from_texture); + Texture *src_tex = texture_owner.get_or_null(p_from_texture); ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, @@ -2746,7 +2849,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, ERR_FAIL_COND_V(p_src_mipmap >= src_tex->mipmaps, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_src_layer >= src_layer_count, ERR_INVALID_PARAMETER); - Texture *dst_tex = texture_owner.getornull(p_to_texture); + Texture *dst_tex = texture_owner.get_or_null(p_to_texture); ERR_FAIL_COND_V(!dst_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER, @@ -2913,7 +3016,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID p_to_texture, uint32_t p_post_barrier) { _THREAD_SAFE_METHOD_ - Texture *src_tex = texture_owner.getornull(p_from_texture); + Texture *src_tex = texture_owner.get_or_null(p_from_texture); ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, @@ -2924,7 +3027,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID ERR_FAIL_COND_V_MSG(src_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Source texture must be 2D (or a slice of a 3D/Cube texture)"); ERR_FAIL_COND_V_MSG(src_tex->samples == TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Source texture must be multisampled."); - Texture *dst_tex = texture_owner.getornull(p_to_texture); + Texture *dst_tex = texture_owner.get_or_null(p_to_texture); ERR_FAIL_COND_V(!dst_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER, @@ -3084,7 +3187,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, uint32_t p_post_barrier) { _THREAD_SAFE_METHOD_ - Texture *src_tex = texture_owner.getornull(p_texture); + Texture *src_tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, @@ -3108,7 +3211,7 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, VkImageLayout clear_layout = (src_tex->layout == VK_IMAGE_LAYOUT_GENERAL) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - // NOTE: Perhaps the valid stages/accesses for a given onwner should be a property of the owner. (Here and places like _get_buffer_from_owner) + // NOTE: Perhaps the valid stages/accesses for a given owner should be a property of the owner. (Here and places like _get_buffer_from_owner) const VkPipelineStageFlags valid_texture_stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; constexpr VkAccessFlags read_access = VK_ACCESS_SHADER_READ_BIT; constexpr VkAccessFlags read_write_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; @@ -3244,58 +3347,69 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f /**** ATTACHMENT ****/ /********************/ -VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, int *r_color_attachment_count) { - Vector<VkAttachmentDescription> attachments; - Vector<VkAttachmentReference> color_references; - Vector<VkAttachmentReference> depth_stencil_references; - Vector<VkAttachmentReference> resolve_references; - - // Set up a dependencies from/to external equivalent to the default (implicit) one, and then amend them +VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count, Vector<TextureSamples> *r_samples) { + // Set up dependencies from/to external equivalent to the default (implicit) one, and then amend them const VkPipelineStageFlags default_access_mask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; // From Section 7.1 of Vulkan API Spec v1.1.148 + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; // From Section 7.1 of Vulkan API Spec v1.1.148 VkPipelineStageFlags reading_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; VkSubpassDependency dependencies[2] = { { VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, default_access_mask, 0 }, { 0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, default_access_mask, 0, 0 } }; VkSubpassDependency &dependency_from_external = dependencies[0]; VkSubpassDependency &dependency_to_external = dependencies[1]; + LocalVector<int32_t> attachment_last_pass; + attachment_last_pass.resize(p_attachments.size()); - for (int i = 0; i < p_format.size(); i++) { - ERR_FAIL_INDEX_V(p_format[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); - ERR_FAIL_INDEX_V(p_format[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); - ERR_FAIL_COND_V_MSG(!(p_format[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)), - VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (depth, stencil or resolve) bit set."); + Vector<VkAttachmentDescription> attachments; + + for (int i = 0; i < p_attachments.size(); i++) { + ERR_FAIL_INDEX_V(p_attachments[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); + ERR_FAIL_INDEX_V(p_attachments[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); + ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)), + VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth, input or stencil) bit set."); VkAttachmentDescription description = {}; description.flags = 0; - description.format = vulkan_formats[p_format[i].format]; - description.samples = rasterization_sample_count[p_format[i].samples]; + description.format = vulkan_formats[p_attachments[i].format]; + description.samples = rasterization_sample_count[p_attachments[i].samples]; - bool is_depth_stencil = p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - bool is_sampled = p_format[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; - bool is_storage = p_format[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; + bool is_sampled = p_attachments[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; + bool is_storage = p_attachments[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; + bool is_depth = p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; // For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write - // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution syncronization vs. - // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that + // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution synchronization vs. + // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that // stage - switch (is_depth_stencil ? p_initial_depth_action : p_initial_color_action) { + + switch (is_depth ? p_initial_depth_action : p_initial_action) { case INITIAL_ACTION_CLEAR_REGION: case INITIAL_ACTION_CLEAR: { - description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - dependency_from_external.srcStageMask |= reading_stages; + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + dependency_from_external.srcStageMask |= reading_stages; + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there + dependency_from_external.srcStageMask |= reading_stages; + } } break; case INITIAL_ACTION_KEEP: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; @@ -3308,11 +3422,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } break; case INITIAL_ACTION_DROP: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; @@ -3326,11 +3440,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } break; case INITIAL_ACTION_CLEAR_REGION_CONTINUE: case INITIAL_ACTION_CONTINUE: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; description.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; @@ -3346,14 +3460,65 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } - switch (is_depth_stencil ? p_final_depth_action : p_final_color_action) { + bool used_last = false; + + { + int last_pass = p_passes.size() - 1; + + if (is_depth) { + //likely missing depth resolve? + if (p_passes[last_pass].depth_attachment == i) { + used_last = true; + } + } else { + if (p_passes[last_pass].resolve_attachments.size()) { + //if using resolve attachments, check resolve attachments + for (int j = 0; j < p_passes[last_pass].resolve_attachments.size(); j++) { + if (p_passes[last_pass].resolve_attachments[j] == i) { + used_last = true; + break; + } + } + } else { + for (int j = 0; j < p_passes[last_pass].color_attachments.size(); j++) { + if (p_passes[last_pass].color_attachments[j] == i) { + used_last = true; + break; + } + } + } + } + + if (!used_last) { + for (int j = 0; j < p_passes[last_pass].preserve_attachments.size(); j++) { + if (p_passes[last_pass].preserve_attachments[j] == i) { + used_last = true; + break; + } + } + } + } + + FinalAction final_action = p_final_action; + FinalAction final_depth_action = p_final_depth_action; + + if (!used_last) { + if (is_depth) { + final_depth_action = FINAL_ACTION_DISCARD; + + } else { + final_action = FINAL_ACTION_DISCARD; + } + } + + switch (is_depth ? final_depth_action : final_action) { case FINAL_ACTION_READ: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -3366,11 +3531,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } break; case FINAL_ACTION_DISCARD: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -3381,11 +3546,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } break; case FINAL_ACTION_CONTINUE: { - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; description.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; @@ -3401,89 +3566,289 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } + attachment_last_pass[i] = -1; + attachments.push_back(description); + } + + LocalVector<VkSubpassDescription> subpasses; + LocalVector<LocalVector<VkAttachmentReference>> color_reference_array; + LocalVector<LocalVector<VkAttachmentReference>> input_reference_array; + LocalVector<LocalVector<VkAttachmentReference>> resolve_reference_array; + LocalVector<LocalVector<uint32_t>> preserve_reference_array; + LocalVector<VkAttachmentReference> depth_reference_array; + + subpasses.resize(p_passes.size()); + color_reference_array.resize(p_passes.size()); + input_reference_array.resize(p_passes.size()); + resolve_reference_array.resize(p_passes.size()); + preserve_reference_array.resize(p_passes.size()); + depth_reference_array.resize(p_passes.size()); + + LocalVector<VkSubpassDependency> subpass_dependencies; + + for (int i = 0; i < p_passes.size(); i++) { + const FramebufferPass *pass = &p_passes[i]; + + LocalVector<VkAttachmentReference> &color_references = color_reference_array[i]; + + TextureSamples texture_samples = TEXTURE_SAMPLES_1; + bool is_multisample_first = true; - VkAttachmentReference reference; - reference.attachment = i; + for (int j = 0; j < pass->color_attachments.size(); j++) { + int32_t attachment = pass->color_attachments[j]; + VkAttachmentReference reference; + if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { + reference.attachment = VK_ATTACHMENT_UNUSED; + reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), color attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as color attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + if (is_multisample_first) { + texture_samples = p_attachments[attachment].samples; + is_multisample_first = false; + } else { + ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples."); + } + reference.attachment = attachment; + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachment_last_pass[attachment] = i; + } color_references.push_back(reference); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - depth_stencil_references.push_back(reference); - } else if (p_format[i].usage_flags & TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT) { - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + + LocalVector<VkAttachmentReference> &input_references = input_reference_array[i]; + + for (int j = 0; j < pass->input_attachments.size(); j++) { + int32_t attachment = pass->input_attachments[j]; + VkAttachmentReference reference; + if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { + reference.attachment = VK_ATTACHMENT_UNUSED; + reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), input attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it isn't marked as an input texture."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + reference.attachment = attachment; + reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment_last_pass[attachment] = i; + } + input_references.push_back(reference); + } + + LocalVector<VkAttachmentReference> &resolve_references = resolve_reference_array[i]; + + if (pass->resolve_attachments.size() > 0) { + ERR_FAIL_COND_V_MSG(pass->resolve_attachments.size() != pass->color_attachments.size(), VK_NULL_HANDLE, "The amount of resolve attachments (" + itos(pass->resolve_attachments.size()) + ") must match the number of color attachments (" + itos(pass->color_attachments.size()) + ")."); + ERR_FAIL_COND_V_MSG(texture_samples == TEXTURE_SAMPLES_1, VK_NULL_HANDLE, "Resolve attachments specified, but color attachments are not multisample."); + } + for (int j = 0; j < pass->resolve_attachments.size(); j++) { + int32_t attachment = pass->resolve_attachments[j]; + VkAttachmentReference reference; + if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { + reference.attachment = VK_ATTACHMENT_UNUSED; + reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + ")."); + ERR_FAIL_COND_V_MSG(pass->color_attachments[j] == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + "), the respective color attachment is marked as unused."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment, it isn't marked as a color texture."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + bool multisample = p_attachments[attachment].samples > TEXTURE_SAMPLES_1; + ERR_FAIL_COND_V_MSG(multisample, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachments can't be multisample."); + reference.attachment = attachment; + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment_last_pass[attachment] = i; + } resolve_references.push_back(reference); - // if resolves are done, we need to ensure the copy is safe - dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; - dependency_to_external.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; + } + + VkAttachmentReference &depth_stencil_reference = depth_reference_array[i]; + + if (pass->depth_attachment != FramebufferPass::ATTACHMENT_UNUSED) { + int32_t attachment = pass->depth_attachment; + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), depth attachment."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not a depth attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + depth_stencil_reference.attachment = attachment; + depth_stencil_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachment_last_pass[attachment] = i; + + if (is_multisample_first) { + texture_samples = p_attachments[attachment].samples; + is_multisample_first = false; + } else { + ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples including the depth."); + } + + } else { + depth_stencil_reference.attachment = VK_ATTACHMENT_UNUSED; + depth_stencil_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + LocalVector<uint32_t> &preserve_references = preserve_reference_array[i]; + + for (int j = 0; j < pass->preserve_attachments.size(); j++) { + int32_t attachment = pass->preserve_attachments[j]; + + ERR_FAIL_COND_V_MSG(attachment == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + "). Preserve attachments can't be unused."); + + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + ")."); + + if (attachment_last_pass[attachment] != i) { + //preserve can still be used to keep depth or color from being discarded after use + attachment_last_pass[attachment] = i; + preserve_references.push_back(attachment); + } + } + + VkSubpassDescription &subpass = subpasses[i]; + subpass.flags = 0; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.inputAttachmentCount = input_references.size(); + if (input_references.size()) { + subpass.pInputAttachments = input_references.ptr(); + } else { + subpass.pInputAttachments = nullptr; + } + subpass.colorAttachmentCount = color_references.size(); + if (color_references.size()) { + subpass.pColorAttachments = color_references.ptr(); + } else { + subpass.pColorAttachments = nullptr; + } + if (depth_stencil_reference.attachment != VK_ATTACHMENT_UNUSED) { + subpass.pDepthStencilAttachment = &depth_stencil_reference; + } else { + subpass.pDepthStencilAttachment = nullptr; + } + + if (resolve_references.size()) { + subpass.pResolveAttachments = resolve_references.ptr(); } else { - ERR_FAIL_V_MSG(VK_NULL_HANDLE, "Texture index " + itos(i) + " is neither color, depth stencil or resolve so it can't be used as attachment."); + subpass.pResolveAttachments = nullptr; } + subpass.preserveAttachmentCount = preserve_references.size(); + if (preserve_references.size()) { + subpass.pPreserveAttachments = preserve_references.ptr(); + } else { + subpass.pPreserveAttachments = nullptr; + } + + if (r_samples) { + r_samples->push_back(texture_samples); + } + + if (i > 0) { + VkSubpassDependency dependency; + dependency.srcSubpass = i - 1; + dependency.dstSubpass = i; + dependency.srcStageMask = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + + dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + subpass_dependencies.push_back(dependency); + } + /* // NOTE: Big Mallet Approach -- any layout transition causes a full barrier if (reference.layout != description.initialLayout) { - // NOTE: this should be smarter based on the textures knowledge of it's previous role + // NOTE: this should be smarter based on the texture's knowledge of its previous role dependency_from_external.srcStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; dependency_from_external.srcAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; } if (reference.layout != description.finalLayout) { - // NOTE: this should be smarter based on the textures knowledge of it's subsequent role + // NOTE: this should be smarter based on the texture's knowledge of its subsequent role dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; dependency_to_external.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; } + */ } - ERR_FAIL_COND_V_MSG(depth_stencil_references.size() > 1, VK_NULL_HANDLE, - "Formats can only have one depth/stencil attachment, supplied (" + itos(depth_stencil_references.size()) + ")."); - - ERR_FAIL_COND_V_MSG(resolve_references.size() > 1, VK_NULL_HANDLE, - "Formats can only have one resolve attachment, supplied (" + itos(resolve_references.size()) + ")."); - - VkSubpassDescription subpass; - subpass.flags = 0; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.inputAttachmentCount = 0; //unsupported for now - subpass.pInputAttachments = nullptr; - subpass.colorAttachmentCount = color_references.size(); - subpass.pColorAttachments = color_references.ptr(); - subpass.pDepthStencilAttachment = depth_stencil_references.ptr(); - subpass.pResolveAttachments = resolve_references.ptr(); - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - VkRenderPassCreateInfo render_pass_create_info; render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; render_pass_create_info.pNext = nullptr; render_pass_create_info.flags = 0; render_pass_create_info.attachmentCount = attachments.size(); render_pass_create_info.pAttachments = attachments.ptr(); - render_pass_create_info.subpassCount = 1; - render_pass_create_info.pSubpasses = &subpass; + render_pass_create_info.subpassCount = subpasses.size(); + render_pass_create_info.pSubpasses = subpasses.ptr(); // Commenting this because it seems it just avoids raster and compute to work at the same time. // Other barriers seem to be protecting the render pass fine. // render_pass_create_info.dependencyCount = 2; // render_pass_create_info.pDependencies = dependencies; - render_pass_create_info.dependencyCount = 0; - render_pass_create_info.pDependencies = nullptr; + render_pass_create_info.dependencyCount = subpass_dependencies.size(); + if (subpass_dependencies.size()) { + render_pass_create_info.pDependencies = subpass_dependencies.ptr(); + } else { + render_pass_create_info.pDependencies = nullptr; + } + + // These are only used if we use multiview but we need to define them in scope. + const uint32_t view_mask = (1 << p_view_count) - 1; + const uint32_t correlation_mask = (1 << p_view_count) - 1; + Vector<uint32_t> view_masks; + VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info; + + if (p_view_count > 1) { + const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities(); + + // For now this only works with multiview! + ERR_FAIL_COND_V_MSG(!capabilities.is_supported, VK_NULL_HANDLE, "Multiview not supported"); + + // Make sure we limit this to the number of views we support. + ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass"); + + // Set view masks for each subpass + for (uint32_t i = 0; i < subpasses.size(); i++) { + view_masks.push_back(view_mask); + } + + render_pass_multiview_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; + render_pass_multiview_create_info.pNext = nullptr; + render_pass_multiview_create_info.subpassCount = subpasses.size(); + render_pass_multiview_create_info.pViewMasks = view_masks.ptr(); + render_pass_multiview_create_info.dependencyCount = 0; + render_pass_multiview_create_info.pViewOffsets = nullptr; + render_pass_multiview_create_info.correlationMaskCount = 1; + render_pass_multiview_create_info.pCorrelationMasks = &correlation_mask; + + render_pass_create_info.pNext = &render_pass_multiview_create_info; + } VkRenderPass render_pass; VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass failed with error " + itos(res) + "."); - if (r_color_attachment_count) { - *r_color_attachment_count = color_references.size(); - } return render_pass; } -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format) { +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count) { + FramebufferPass pass; + for (int i = 0; i < p_format.size(); i++) { + if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + pass.depth_attachment = i; + } else { + pass.color_attachments.push_back(i); + } + } + + Vector<FramebufferPass> passes; + passes.push_back(pass); + return framebuffer_format_create_multipass(p_format, passes, p_view_count); +} +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ FramebufferFormatKey key; - key.attachments = p_format; + key.attachments = p_attachments; + key.passes = p_passes; + key.view_count = p_view_count; const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { @@ -3491,8 +3856,8 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c return E->get(); } - int color_references; - VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, &color_references); //actions don't matter for this use case + Vector<TextureSamples> samples; + VkRenderPass render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, p_view_count, &samples); //actions don't matter for this use case if (render_pass == VK_NULL_HANDLE) { //was likely invalid return INVALID_ID; @@ -3502,15 +3867,16 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c E = framebuffer_format_cache.insert(key, id); FramebufferFormat fb_format; fb_format.E = E; - fb_format.color_attachments = color_references; fb_format.render_pass = render_pass; - fb_format.samples = p_format[0].samples; + fb_format.pass_samples = samples; + fb_format.view_count = p_view_count; framebuffer_formats[id] = fb_format; return id; } RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_empty(TextureSamples p_samples) { FramebufferFormatKey key; + key.passes.push_back(FramebufferPass()); const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { @@ -3544,7 +3910,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c VkRenderPass render_pass; VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); - ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass for empty fb failed with error " + itos(res) + "."); + ERR_FAIL_COND_V_MSG(res, 0, "vkCreateRenderPass for empty fb failed with error " + itos(res) + "."); if (render_pass == VK_NULL_HANDLE) { //was likely invalid return INVALID_ID; @@ -3556,18 +3922,18 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c FramebufferFormat fb_format; fb_format.E = E; - fb_format.color_attachments = 0; fb_format.render_pass = render_pass; - fb_format.samples = p_samples; + fb_format.pass_samples.push_back(p_samples); framebuffer_formats[id] = fb_format; return id; } -RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format) { +RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass) { Map<FramebufferFormatID, FramebufferFormat>::Element *E = framebuffer_formats.find(p_format); ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1); + ERR_FAIL_COND_V(p_pass >= uint32_t(E->get().pass_samples.size()), TEXTURE_SAMPLES_1); - return E->get().samples; + return E->get().pass_samples[p_pass]; } /***********************/ @@ -3580,20 +3946,47 @@ RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, Textur framebuffer.format_id = framebuffer_format_create_empty(p_samples); ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID()); framebuffer.size = p_size; + framebuffer.view_count = 1; return framebuffer_owner.make_rid(framebuffer); } -RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check) { +RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) { + _THREAD_SAFE_METHOD_ + + FramebufferPass pass; + + for (int i = 0; i < p_texture_attachments.size(); i++) { + Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); + ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + + if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + pass.depth_attachment = i; + } else { + pass.color_attachments.push_back(i); + } + } + + Vector<FramebufferPass> passes; + passes.push_back(pass); + + return framebuffer_create_multipass(p_texture_attachments, passes, p_format_check, p_view_count); +} + +RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ Vector<AttachmentFormat> attachments; Size2i size; for (int i = 0; i < p_texture_attachments.size(); i++) { - Texture *texture = texture_owner.getornull(p_texture_attachments[i]); + Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); + ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + if (i == 0) { size.width = texture->width; size.height = texture->height; @@ -3609,7 +4002,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac attachments.push_back(af); } - FramebufferFormatID format_id = framebuffer_format_create(attachments); + FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count); if (format_id == INVALID_ID) { return RID(); } @@ -3621,6 +4014,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac framebuffer.format_id = format_id; framebuffer.texture_ids = p_texture_attachments; framebuffer.size = size; + framebuffer.view_count = p_view_count; RID id = framebuffer_owner.make_rid(framebuffer); @@ -3634,7 +4028,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_get_format(RID p_framebuffer) { _THREAD_SAFE_METHOD_ - Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer); + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); ERR_FAIL_COND_V(!framebuffer, INVALID_ID); return framebuffer->format_id; @@ -3784,7 +4178,7 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo vertex_array.description = p_vertex_format; vertex_array.max_instances_allowed = 0xFFFFFFFF; //by default as many as you want for (int i = 0; i < p_src_buffers.size(); i++) { - Buffer *buffer = vertex_buffer_owner.getornull(p_src_buffers[i]); + Buffer *buffer = vertex_buffer_owner.get_or_null(p_src_buffers[i]); //validate with buffer { @@ -3880,7 +4274,7 @@ RID RenderingDeviceVulkan::index_array_create(RID p_index_buffer, uint32_t p_ind ERR_FAIL_COND_V(!index_buffer_owner.owns(p_index_buffer), RID()); - IndexBuffer *index_buffer = index_buffer_owner.getornull(p_index_buffer); + IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_index_buffer); ERR_FAIL_COND_V(p_index_count == 0, RID()); ERR_FAIL_COND_V(p_index_offset + p_index_count > index_buffer->index_count, RID()); @@ -3924,7 +4318,7 @@ static VkShaderStageFlagBits shader_stage_masks[RenderingDevice::SHADER_STAGE_MA String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) { String ret; - const Shader *shader = shader_owner.getornull(p_shader); + const Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, String()); for (int i = 0; i < shader->sets.size(); i++) { if (p_set >= 0 && i != p_set) { @@ -3932,7 +4326,7 @@ String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) { } for (int j = 0; j < shader->sets[i].uniform_info.size(); j++) { const UniformInfo &ui = shader->sets[i].uniform_info[j]; - if (ret != String()) { + if (!ret.is_empty()) { ret += "\n"; } ret += "Set: " + itos(i) + " Binding: " + itos(ui.binding) + " Type: " + shader_uniform_names[ui.type] + " Length: " + itos(ui.length); @@ -4133,51 +4527,91 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa } #endif -RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages) { - //descriptor layouts - Vector<Vector<VkDescriptorSetLayoutBinding>> set_bindings; - Vector<Vector<UniformInfo>> uniform_info; - Shader::PushConstant push_constant; - push_constant.push_constant_size = 0; - push_constant.push_constants_vk_stage = 0; +//version 1: initial +//version 2: Added shader name - uint32_t vertex_input_mask = 0; +#define SHADER_BINARY_VERSION 2 - uint32_t fragment_outputs = 0; +String RenderingDeviceVulkan::shader_get_binary_cache_key() const { + return "Vulkan-SV" + itos(SHADER_BINARY_VERSION); +} - uint32_t stages_processed = 0; +struct RenderingDeviceVulkanShaderBinaryDataBinding { + uint32_t type; + uint32_t binding; + uint32_t stages; + uint32_t length; //size of arrays (in total elements), or ubos (in bytes * total elements) +}; - bool is_compute = false; +struct RenderingDeviceVulkanShaderBinarySpecializationConstant { + uint32_t type; + uint32_t constant_id; + union { + uint32_t int_value; + float float_value; + bool bool_value; + }; + uint32_t stage_flags; +}; + +struct RenderingDeviceVulkanShaderBinaryData { + uint32_t vertex_input_mask; + uint32_t fragment_outputs; + uint32_t specialization_constant_count; + uint32_t is_compute; + uint32_t compute_local_size[3]; + uint32_t set_count; + uint32_t push_constant_size; + uint32_t push_constants_vk_stage; + uint32_t stage_count; + uint32_t shader_name_len; +}; - uint32_t compute_local_size[3] = { 0, 0, 0 }; +Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name) { + RenderingDeviceVulkanShaderBinaryData binary_data; + binary_data.vertex_input_mask = 0; + binary_data.fragment_outputs = 0; + binary_data.specialization_constant_count = 0; + binary_data.is_compute = 0; + binary_data.compute_local_size[0] = 0; + binary_data.compute_local_size[1] = 0; + binary_data.compute_local_size[2] = 0; + binary_data.set_count = 0; + binary_data.push_constant_size = 0; + binary_data.push_constants_vk_stage = 0; + + Vector<Vector<RenderingDeviceVulkanShaderBinaryDataBinding>> uniform_info; //set bindings + Vector<RenderingDeviceVulkanShaderBinarySpecializationConstant> specialization_constants; - for (int i = 0; i < p_stages.size(); i++) { - if (p_stages[i].shader_stage == SHADER_STAGE_COMPUTE) { - is_compute = true; - ERR_FAIL_COND_V_MSG(p_stages.size() != 1, RID(), + uint32_t stages_processed = 0; + + for (int i = 0; i < p_spirv.size(); i++) { + if (p_spirv[i].shader_stage == SHADER_STAGE_COMPUTE) { + binary_data.is_compute = true; + ERR_FAIL_COND_V_MSG(p_spirv.size() != 1, Vector<uint8_t>(), "Compute shaders can only receive one stage, dedicated to compute."); } - ERR_FAIL_COND_V_MSG(stages_processed & (1 << p_stages[i].shader_stage), RID(), - "Stage " + String(shader_stage_names[p_stages[i].shader_stage]) + " submitted more than once."); + ERR_FAIL_COND_V_MSG(stages_processed & (1 << p_spirv[i].shader_stage), Vector<uint8_t>(), + "Stage " + String(shader_stage_names[p_spirv[i].shader_stage]) + " submitted more than once."); { SpvReflectShaderModule module; - const uint8_t *spirv = p_stages[i].spir_v.ptr(); - SpvReflectResult result = spvReflectCreateShaderModule(p_stages[i].spir_v.size(), spirv, &module); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed parsing shader."); - - if (is_compute) { - compute_local_size[0] = module.entry_points->local_size.x; - compute_local_size[1] = module.entry_points->local_size.y; - compute_local_size[2] = module.entry_points->local_size.z; + const uint8_t *spirv = p_spirv[i].spir_v.ptr(); + SpvReflectResult result = spvReflectCreateShaderModule(p_spirv[i].spir_v.size(), spirv, &module); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed parsing shader."); + + if (binary_data.is_compute) { + binary_data.compute_local_size[0] = module.entry_points->local_size.x; + binary_data.compute_local_size[1] = module.entry_points->local_size.y; + binary_data.compute_local_size[2] = module.entry_points->local_size.z; } uint32_t binding_count = 0; result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating descriptor bindings."); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating descriptor bindings."); - uint32_t stage = p_stages[i].shader_stage; + uint32_t stage = p_spirv[i].shader_stage; if (binding_count > 0) { //Parse bindings @@ -4186,56 +4620,47 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages bindings.resize(binding_count); result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed getting descriptor bindings."); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed getting descriptor bindings."); for (uint32_t j = 0; j < binding_count; j++) { const SpvReflectDescriptorBinding &binding = *bindings[j]; - VkDescriptorSetLayoutBinding layout_binding; - UniformInfo info; + RenderingDeviceVulkanShaderBinaryDataBinding info; bool need_array_dimensions = false; bool need_block_size = false; switch (binding.descriptor_type) { case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; info.type = UNIFORM_TYPE_SAMPLER; need_array_dimensions = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; need_array_dimensions = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; info.type = UNIFORM_TYPE_TEXTURE; need_array_dimensions = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; info.type = UNIFORM_TYPE_IMAGE; need_array_dimensions = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; info.type = UNIFORM_TYPE_TEXTURE_BUFFER; need_array_dimensions = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; info.type = UNIFORM_TYPE_IMAGE_BUFFER; need_array_dimensions = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; info.type = UNIFORM_TYPE_UNIFORM_BUFFER; need_block_size = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; info.type = UNIFORM_TYPE_STORAGE_BUFFER; need_block_size = true; } break; @@ -4248,8 +4673,8 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages continue; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { - layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; info.type = UNIFORM_TYPE_INPUT_ATTACHMENT; + need_array_dimensions = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: { ERR_PRINT("Acceleration structure not supported."); @@ -4270,42 +4695,35 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages } } - layout_binding.descriptorCount = info.length; - } else if (need_block_size) { info.length = binding.block.size; - layout_binding.descriptorCount = 1; } else { info.length = 0; - layout_binding.descriptorCount = 1; } info.binding = binding.binding; uint32_t set = binding.set; - //print_line("Stage: " + String(shader_stage_names[stage]) + " set=" + itos(set) + " binding=" + itos(info.binding) + " type=" + shader_uniform_names[info.type] + " length=" + itos(info.length)); - - ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, RID(), + ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, Vector<uint8_t>(), "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ")."); - ERR_FAIL_COND_V_MSG(set >= limits.maxBoundDescriptorSets, RID(), + ERR_FAIL_COND_V_MSG(set >= limits.maxBoundDescriptorSets, Vector<uint8_t>(), "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."); - if (set < (uint32_t)set_bindings.size()) { + if (set < (uint32_t)uniform_info.size()) { //check if this already exists bool exists = false; - for (int k = 0; k < set_bindings[set].size(); k++) { - if (set_bindings[set][k].binding == (uint32_t)info.binding) { + for (int k = 0; k < uniform_info[set].size(); k++) { + if (uniform_info[set][k].binding == (uint32_t)info.binding) { //already exists, verify that it's the same type - ERR_FAIL_COND_V_MSG(set_bindings[set][k].descriptorType != layout_binding.descriptorType, RID(), + ERR_FAIL_COND_V_MSG(uniform_info[set][k].type != info.type, Vector<uint8_t>(), "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type."); //also, verify that it's the same size - ERR_FAIL_COND_V_MSG(set_bindings[set][k].descriptorCount != layout_binding.descriptorCount || uniform_info[set][k].length != info.length, RID(), + ERR_FAIL_COND_V_MSG(uniform_info[set][k].length != info.length, Vector<uint8_t>(), "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size."); //just append stage mask and return - set_bindings.write[set].write[k].stageFlags |= shader_stage_masks[stage]; uniform_info.write[set].write[k].stages |= 1 << stage; exists = true; } @@ -4316,40 +4734,90 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages } } - layout_binding.binding = info.binding; - layout_binding.stageFlags = shader_stage_masks[stage]; - layout_binding.pImmutableSamplers = nullptr; //no support for this yet - info.stages = 1 << stage; - info.binding = info.binding; - if (set >= (uint32_t)set_bindings.size()) { - set_bindings.resize(set + 1); + if (set >= (uint32_t)uniform_info.size()) { uniform_info.resize(set + 1); } - set_bindings.write[set].push_back(layout_binding); uniform_info.write[set].push_back(info); } } + { + //specialization constants + + uint32_t sc_count = 0; + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating specialization constants."); + + if (sc_count) { + Vector<SpvReflectSpecializationConstant *> spec_constants; + spec_constants.resize(sc_count); + + result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, spec_constants.ptrw()); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining specialization constants."); + + for (uint32_t j = 0; j < sc_count; j++) { + int32_t existing = -1; + RenderingDeviceVulkanShaderBinarySpecializationConstant sconst; + SpvReflectSpecializationConstant *spc = spec_constants[j]; + + sconst.constant_id = spc->constant_id; + sconst.int_value = 0.0; // clear previous value JIC + switch (spc->constant_type) { + case SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL: { + sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + sconst.bool_value = spc->default_value.int_bool_value != 0; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_INT: { + sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; + sconst.int_value = spc->default_value.int_bool_value; + } break; + case SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT: { + sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; + sconst.float_value = spc->default_value.float_value; + } break; + } + sconst.stage_flags = 1 << p_spirv[i].shader_stage; + + for (int k = 0; k < specialization_constants.size(); k++) { + if (specialization_constants[k].constant_id == sconst.constant_id) { + ERR_FAIL_COND_V_MSG(specialization_constants[k].type != sconst.type, Vector<uint8_t>(), "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their types differ."); + ERR_FAIL_COND_V_MSG(specialization_constants[k].int_value != sconst.int_value, Vector<uint8_t>(), "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their default values differ."); + existing = k; + break; + } + } + + if (existing > 0) { + specialization_constants.write[existing].stage_flags |= sconst.stage_flags; + } else { + specialization_constants.push_back(sconst); + } + } + } + } + if (stage == SHADER_STAGE_VERTEX) { uint32_t iv_count = 0; result = spvReflectEnumerateInputVariables(&module, &iv_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating input variables."); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating input variables."); if (iv_count) { Vector<SpvReflectInterfaceVariable *> input_vars; input_vars.resize(iv_count); result = spvReflectEnumerateInputVariables(&module, &iv_count, input_vars.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining input variables."); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining input variables."); for (uint32_t j = 0; j < iv_count; j++) { if (input_vars[j] && input_vars[j]->decoration_flags == 0) { //regular input - vertex_input_mask |= (1 << uint32_t(input_vars[j]->location)); + binary_data.vertex_input_mask |= (1 << uint32_t(input_vars[j]->location)); } } } @@ -4358,20 +4826,21 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages if (stage == SHADER_STAGE_FRAGMENT) { uint32_t ov_count = 0; result = spvReflectEnumerateOutputVariables(&module, &ov_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating output variables."); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating output variables."); if (ov_count) { Vector<SpvReflectInterfaceVariable *> output_vars; output_vars.resize(ov_count); result = spvReflectEnumerateOutputVariables(&module, &ov_count, output_vars.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining output variables."); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining output variables."); for (uint32_t j = 0; j < ov_count; j++) { - if (output_vars[j]) { - fragment_outputs = MAX(fragment_outputs, output_vars[j]->location + 1); + const SpvReflectInterfaceVariable *refvar = output_vars[j]; + if (refvar != nullptr && refvar->built_in != SpvBuiltInFragDepth) { + binary_data.fragment_outputs |= 1 << refvar->location; } } } @@ -4379,18 +4848,18 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages uint32_t pc_count = 0; result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, nullptr); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating push constants."); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating push constants."); if (pc_count) { - ERR_FAIL_COND_V_MSG(pc_count > 1, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages."); + ERR_FAIL_COND_V_MSG(pc_count > 1, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages."); Vector<SpvReflectBlockVariable *> pconstants; pconstants.resize(pc_count); result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, pconstants.ptrw()); - ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining push constants."); + ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining push constants."); #if 0 if (pconstants[0] == nullptr) { FileAccess *f = FileAccess::open("res://popo.spv", FileAccess::WRITE); @@ -4399,11 +4868,11 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages } #endif - ERR_FAIL_COND_V_MSG(push_constant.push_constant_size && push_constant.push_constant_size != pconstants[0]->size, RID(), - "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "': Push constant block must be the same across shader stages."); + ERR_FAIL_COND_V_MSG(binary_data.push_constant_size && binary_data.push_constant_size != pconstants[0]->size, Vector<uint8_t>(), + "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Push constant block must be the same across shader stages."); - push_constant.push_constant_size = pconstants[0]->size; - push_constant.push_constants_vk_stage |= shader_stage_masks[stage]; + binary_data.push_constant_size = pconstants[0]->size; + binary_data.push_constants_vk_stage |= shader_stage_masks[stage]; //print_line("Stage: " + String(shader_stage_names[stage]) + " push constant of size=" + itos(push_constant.push_constant_size)); } @@ -4412,9 +4881,317 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages spvReflectDestroyShaderModule(&module); } - stages_processed |= (1 << p_stages[i].shader_stage); + stages_processed |= (1 << p_spirv[i].shader_stage); + } + + Vector<Vector<uint8_t>> compressed_stages; + Vector<uint32_t> smolv_size; + Vector<uint32_t> zstd_size; //if 0, stdno t used + + uint32_t stages_binary_size = 0; + + bool strip_debug = false; + + for (int i = 0; i < p_spirv.size(); i++) { + smolv::ByteArray smolv; + if (!smolv::Encode(p_spirv[i].spir_v.ptr(), p_spirv[i].spir_v.size(), smolv, strip_debug ? smolv::kEncodeFlagStripDebugInfo : 0)) { + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Error compressing shader stage :" + String(shader_stage_names[p_spirv[i].shader_stage])); + } else { + smolv_size.push_back(smolv.size()); + { //zstd + Vector<uint8_t> zstd; + zstd.resize(Compression::get_max_compressed_buffer_size(smolv.size(), Compression::MODE_ZSTD)); + int dst_size = Compression::compress(zstd.ptrw(), &smolv[0], smolv.size(), Compression::MODE_ZSTD); + + if (dst_size > 0 && (uint32_t)dst_size < smolv.size()) { + zstd_size.push_back(dst_size); + zstd.resize(dst_size); + compressed_stages.push_back(zstd); + } else { + Vector<uint8_t> smv; + smv.resize(smolv.size()); + memcpy(smv.ptrw(), &smolv[0], smolv.size()); + zstd_size.push_back(0); //not using zstd + compressed_stages.push_back(smv); + } + } + } + uint32_t s = compressed_stages[i].size(); + if (s % 4 != 0) { + s += 4 - (s % 4); + } + stages_binary_size += s; + } + + binary_data.specialization_constant_count = specialization_constants.size(); + binary_data.set_count = uniform_info.size(); + binary_data.stage_count = p_spirv.size(); + + CharString shader_name_utf = p_shader_name.utf8(); + + binary_data.shader_name_len = shader_name_utf.length(); + + uint32_t total_size = sizeof(uint32_t) * 3; //header + version + main datasize; + total_size += sizeof(RenderingDeviceVulkanShaderBinaryData); + + total_size += binary_data.shader_name_len; + + if ((binary_data.shader_name_len % 4) != 0) { //alignment rules are really strange + total_size += 4 - (binary_data.shader_name_len % 4); } + for (int i = 0; i < uniform_info.size(); i++) { + total_size += sizeof(uint32_t); + total_size += uniform_info[i].size() * sizeof(RenderingDeviceVulkanShaderBinaryDataBinding); + } + + total_size += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size(); + + total_size += compressed_stages.size() * sizeof(uint32_t) * 3; //sizes + total_size += stages_binary_size; + + Vector<uint8_t> ret; + ret.resize(total_size); + uint32_t offset = 0; + { + uint8_t *binptr = ret.ptrw(); + binptr[0] = 'G'; + binptr[1] = 'V'; + binptr[2] = 'B'; + binptr[3] = 'D'; //godot vulkan binary data + offset += 4; + encode_uint32(SHADER_BINARY_VERSION, binptr + offset); + offset += sizeof(uint32_t); + encode_uint32(sizeof(RenderingDeviceVulkanShaderBinaryData), binptr + offset); + offset += sizeof(uint32_t); + memcpy(binptr + offset, &binary_data, sizeof(RenderingDeviceVulkanShaderBinaryData)); + offset += sizeof(RenderingDeviceVulkanShaderBinaryData); + memcpy(binptr + offset, shader_name_utf.ptr(), binary_data.shader_name_len); + offset += binary_data.shader_name_len; + + if ((binary_data.shader_name_len % 4) != 0) { //alignment rules are really strange + offset += 4 - (binary_data.shader_name_len % 4); + } + + for (int i = 0; i < uniform_info.size(); i++) { + int count = uniform_info[i].size(); + encode_uint32(count, binptr + offset); + offset += sizeof(uint32_t); + if (count > 0) { + memcpy(binptr + offset, uniform_info[i].ptr(), sizeof(RenderingDeviceVulkanShaderBinaryDataBinding) * count); + offset += sizeof(RenderingDeviceVulkanShaderBinaryDataBinding) * count; + } + } + + if (specialization_constants.size()) { + memcpy(binptr + offset, specialization_constants.ptr(), sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size()); + offset += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size(); + } + + for (int i = 0; i < compressed_stages.size(); i++) { + encode_uint32(p_spirv[i].shader_stage, binptr + offset); + offset += sizeof(uint32_t); + encode_uint32(smolv_size[i], binptr + offset); + offset += sizeof(uint32_t); + encode_uint32(zstd_size[i], binptr + offset); + offset += sizeof(uint32_t); + memcpy(binptr + offset, compressed_stages[i].ptr(), compressed_stages[i].size()); + + uint32_t s = compressed_stages[i].size(); + + if (s % 4 != 0) { + s += 4 - (s % 4); + } + + offset += s; + } + + ERR_FAIL_COND_V(offset != (uint32_t)ret.size(), Vector<uint8_t>()); + } + + return ret; +} + +RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary) { + const uint8_t *binptr = p_shader_binary.ptr(); + uint32_t binsize = p_shader_binary.size(); + + uint32_t read_offset = 0; + //consistency check + ERR_FAIL_COND_V(binsize < sizeof(uint32_t) * 3 + sizeof(RenderingDeviceVulkanShaderBinaryData), RID()); + ERR_FAIL_COND_V(binptr[0] != 'G' || binptr[1] != 'V' || binptr[2] != 'B' || binptr[3] != 'D', RID()); + + uint32_t bin_version = decode_uint32(binptr + 4); + ERR_FAIL_COND_V(bin_version > SHADER_BINARY_VERSION, RID()); + + uint32_t bin_data_size = decode_uint32(binptr + 8); + + const RenderingDeviceVulkanShaderBinaryData &binary_data = *(const RenderingDeviceVulkanShaderBinaryData *)(binptr + 12); + + Shader::PushConstant push_constant; + push_constant.push_constant_size = binary_data.push_constant_size; + push_constant.push_constants_vk_stage = binary_data.push_constants_vk_stage; + + uint32_t vertex_input_mask = binary_data.vertex_input_mask; + + uint32_t fragment_outputs = binary_data.fragment_outputs; + + bool is_compute = binary_data.is_compute; + + uint32_t compute_local_size[3] = { binary_data.compute_local_size[0], binary_data.compute_local_size[1], binary_data.compute_local_size[2] }; + + read_offset += sizeof(uint32_t) * 3 + bin_data_size; + + String name; + + if (binary_data.shader_name_len) { + name.parse_utf8((const char *)(binptr + read_offset), binary_data.shader_name_len); + read_offset += binary_data.shader_name_len; + if ((binary_data.shader_name_len % 4) != 0) { //alignment rules are really strange + read_offset += 4 - (binary_data.shader_name_len % 4); + } + } + + Vector<Vector<VkDescriptorSetLayoutBinding>> set_bindings; + Vector<Vector<UniformInfo>> uniform_info; + + set_bindings.resize(binary_data.set_count); + uniform_info.resize(binary_data.set_count); + + for (uint32_t i = 0; i < binary_data.set_count; i++) { + ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) >= binsize, RID()); + uint32_t set_count = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + const RenderingDeviceVulkanShaderBinaryDataBinding *set_ptr = (const RenderingDeviceVulkanShaderBinaryDataBinding *)(binptr + read_offset); + uint32_t set_size = set_count * sizeof(RenderingDeviceVulkanShaderBinaryDataBinding); + ERR_FAIL_COND_V(read_offset + set_size >= binsize, RID()); + + for (uint32_t j = 0; j < set_count; j++) { + UniformInfo info; + info.type = UniformType(set_ptr[j].type); + info.length = set_ptr[j].length; + info.binding = set_ptr[j].binding; + info.stages = set_ptr[j].stages; + + VkDescriptorSetLayoutBinding layout_binding; + layout_binding.pImmutableSamplers = nullptr; + layout_binding.binding = set_ptr[j].binding; + layout_binding.descriptorCount = 1; + layout_binding.stageFlags = 0; + for (uint32_t k = 0; k < SHADER_STAGE_MAX; k++) { + if (set_ptr[j].stages & (1 << k)) { + layout_binding.stageFlags |= shader_stage_masks[k]; + } + } + + switch (info.type) { + case UNIFORM_TYPE_SAMPLER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_TEXTURE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_IMAGE: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_TEXTURE_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + layout_binding.descriptorCount = set_ptr[j].length; + } break; + case UNIFORM_TYPE_IMAGE_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + } break; + case UNIFORM_TYPE_UNIFORM_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + } break; + case UNIFORM_TYPE_STORAGE_BUFFER: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + } break; + case UNIFORM_TYPE_INPUT_ATTACHMENT: { + layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + } break; + default: { + ERR_FAIL_V(RID()); + } + } + + set_bindings.write[i].push_back(layout_binding); + uniform_info.write[i].push_back(info); + } + + read_offset += set_size; + } + + ERR_FAIL_COND_V(read_offset + binary_data.specialization_constant_count * sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) >= binsize, RID()); + + Vector<Shader::SpecializationConstant> specialization_constants; + + for (uint32_t i = 0; i < binary_data.specialization_constant_count; i++) { + const RenderingDeviceVulkanShaderBinarySpecializationConstant &src_sc = *(const RenderingDeviceVulkanShaderBinarySpecializationConstant *)(binptr + read_offset); + Shader::SpecializationConstant sc; + sc.constant.int_value = src_sc.int_value; + sc.constant.type = PipelineSpecializationConstantType(src_sc.type); + sc.constant.constant_id = src_sc.constant_id; + sc.stage_flags = src_sc.stage_flags; + specialization_constants.push_back(sc); + + read_offset += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant); + } + + Vector<Vector<uint8_t>> stage_spirv_data; + Vector<ShaderStage> stage_type; + + for (uint32_t i = 0; i < binary_data.stage_count; i++) { + ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) * 3 >= binsize, RID()); + uint32_t stage = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + uint32_t smolv_size = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + uint32_t zstd_size = decode_uint32(binptr + read_offset); + read_offset += sizeof(uint32_t); + + uint32_t buf_size = (zstd_size > 0) ? zstd_size : smolv_size; + + Vector<uint8_t> smolv; + const uint8_t *src_smolv = nullptr; + + if (zstd_size > 0) { + //decompress to smolv + smolv.resize(smolv_size); + int dec_smolv_size = Compression::decompress(smolv.ptrw(), smolv.size(), binptr + read_offset, zstd_size, Compression::MODE_ZSTD); + ERR_FAIL_COND_V(dec_smolv_size != (int32_t)smolv_size, RID()); + src_smolv = smolv.ptr(); + } else { + src_smolv = binptr + read_offset; + } + + Vector<uint8_t> spirv; + uint32_t spirv_size = smolv::GetDecodedBufferSize(src_smolv, smolv_size); + spirv.resize(spirv_size); + if (!smolv::Decode(src_smolv, smolv_size, spirv.ptrw(), spirv_size)) { + ERR_FAIL_V_MSG(RID(), "Malformed smolv input uncompressing shader stage:" + String(shader_stage_names[stage])); + } + stage_spirv_data.push_back(spirv); + stage_type.push_back(ShaderStage(stage)); + + if (buf_size % 4 != 0) { + buf_size += 4 - (buf_size % 4); + } + + ERR_FAIL_COND_V(read_offset + buf_size > binsize, RID()); + + read_offset += buf_size; + } + + ERR_FAIL_COND_V(read_offset != binsize, RID()); + //all good, let's create modules _THREAD_SAFE_METHOD_ @@ -4422,23 +5199,25 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages Shader shader; shader.vertex_input_mask = vertex_input_mask; - shader.fragment_outputs = fragment_outputs; + shader.fragment_output_mask = fragment_outputs; shader.push_constant = push_constant; shader.is_compute = is_compute; shader.compute_local_size[0] = compute_local_size[0]; shader.compute_local_size[1] = compute_local_size[1]; shader.compute_local_size[2] = compute_local_size[2]; + shader.specialization_constants = specialization_constants; + shader.name = name; String error_text; bool success = true; - for (int i = 0; i < p_stages.size(); i++) { + for (int i = 0; i < stage_spirv_data.size(); i++) { VkShaderModuleCreateInfo shader_module_create_info; shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; shader_module_create_info.pNext = nullptr; shader_module_create_info.flags = 0; - shader_module_create_info.codeSize = p_stages[i].spir_v.size(); - const uint8_t *r = p_stages[i].spir_v.ptr(); + shader_module_create_info.codeSize = stage_spirv_data[i].size(); + const uint8_t *r = stage_spirv_data[i].ptr(); shader_module_create_info.pCode = (const uint32_t *)r; @@ -4446,7 +5225,7 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages VkResult res = vkCreateShaderModule(device, &shader_module_create_info, nullptr, &module); if (res) { success = false; - error_text = "Error (" + itos(res) + ") creating shader module for stage: " + String(shader_stage_names[p_stages[i].shader_stage]); + error_text = "Error (" + itos(res) + ") creating shader module for stage: " + String(shader_stage_names[stage_type[i]]); break; } @@ -4462,7 +5241,7 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages shader_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stage.pNext = nullptr; shader_stage.flags = 0; - shader_stage.stage = shader_stage_bits[p_stages[i].shader_stage]; + shader_stage.stage = shader_stage_bits[stage_type[i]]; shader_stage.module = module; shader_stage.pName = "main"; shader_stage.pSpecializationInfo = nullptr; @@ -4573,7 +5352,7 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) { _THREAD_SAFE_METHOD_ - const Shader *shader = shader_owner.getornull(p_shader); + const Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, 0); return shader->vertex_input_mask; } @@ -4795,7 +5574,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, ERR_FAIL_COND_V(p_uniforms.size() == 0, RID()); - Shader *shader = shader_owner.getornull(p_shader); + Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RID()); ERR_FAIL_COND_V_MSG(p_shader_set >= (uint32_t)shader->sets.size() || shader->sets[p_shader_set].uniform_info.size() == 0, RID(), @@ -4853,18 +5632,18 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, switch (uniform.uniform_type) { case UNIFORM_TYPE_SAMPLER: { - if (uniform.ids.size() != set_uniform.length) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler elements, so it should be provided equal number of sampler IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler elements, so it should be provided equal number of sampler IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") should provide one ID referencing a sampler (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") should provide one ID referencing a sampler (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorImageInfo> image_info; - for (int j = 0; j < uniform.ids.size(); j++) { - VkSampler *sampler = sampler_owner.getornull(uniform.ids[j]); + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + VkSampler *sampler = sampler_owner.get_or_null(uniform.get_id(j)); ERR_FAIL_COND_V_MSG(!sampler, RID(), "Sampler (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid sampler."); VkDescriptorImageInfo img_info; @@ -4876,31 +5655,31 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size(); + write.descriptorCount = uniform.get_id_count(); write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - type_size = uniform.ids.size(); + type_size = uniform.get_id_count(); } break; case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { - if (uniform.ids.size() != set_uniform.length * 2) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length * 2) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler&texture elements, so it should provided twice the amount of IDs (sampler,texture pairs) to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler&texture elements, so it should provided twice the amount of IDs (sampler,texture pairs) to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorImageInfo> image_info; - for (int j = 0; j < uniform.ids.size(); j += 2) { - VkSampler *sampler = sampler_owner.getornull(uniform.ids[j + 0]); + for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { + VkSampler *sampler = sampler_owner.get_or_null(uniform.get_id(j + 0)); ERR_FAIL_COND_V_MSG(!sampler, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); - Texture *texture = texture_owner.getornull(uniform.ids[j + 1]); + Texture *texture = texture_owner.get_or_null(uniform.get_id(j + 1)); ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), @@ -4910,10 +5689,10 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, img_info.sampler = *sampler; img_info.imageView = texture->view; - if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) { + if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) { UniformSet::AttachableTexture attachable_texture; attachable_texture.bind = set_uniform.binding; - attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.ids[j + 1]; + attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j + 1); attachable_textures.push_back(attachable_texture); } @@ -4922,7 +5701,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, mutable_sampled_textures.push_back(texture); } if (texture->owner.is_valid()) { - texture = texture_owner.getornull(texture->owner); + texture = texture_owner.get_or_null(texture->owner); ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen } @@ -4932,28 +5711,28 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size() / 2; + write.descriptorCount = uniform.get_id_count() / 2; write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - type_size = uniform.ids.size() / 2; + type_size = uniform.get_id_count() / 2; } break; case UNIFORM_TYPE_TEXTURE: { - if (uniform.ids.size() != set_uniform.length) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorImageInfo> image_info; - for (int j = 0; j < uniform.ids.size(); j++) { - Texture *texture = texture_owner.getornull(uniform.ids[j]); + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), @@ -4963,10 +5742,10 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, img_info.sampler = VK_NULL_HANDLE; img_info.imageView = texture->view; - if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) { + if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) { UniformSet::AttachableTexture attachable_texture; attachable_texture.bind = set_uniform.binding; - attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.ids[j]; + attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j); attachable_textures.push_back(attachable_texture); } @@ -4976,7 +5755,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } if (texture->owner.is_valid()) { - texture = texture_owner.getornull(texture->owner); + texture = texture_owner.get_or_null(texture->owner); ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen } @@ -4986,27 +5765,27 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size(); + write.descriptorCount = uniform.get_id_count(); write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - type_size = uniform.ids.size(); + type_size = uniform.get_id_count(); } break; case UNIFORM_TYPE_IMAGE: { - if (uniform.ids.size() != set_uniform.length) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorImageInfo> image_info; - for (int j = 0; j < uniform.ids.size(); j++) { - Texture *texture = texture_owner.getornull(uniform.ids[j]); + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); ERR_FAIL_COND_V_MSG(!texture, RID(), "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); @@ -5024,7 +5803,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } if (texture->owner.is_valid()) { - texture = texture_owner.getornull(texture->owner); + texture = texture_owner.get_or_null(texture->owner); ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen } @@ -5034,29 +5813,29 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size(); + write.descriptorCount = uniform.get_id_count(); write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - type_size = uniform.ids.size(); + type_size = uniform.get_id_count(); } break; case UNIFORM_TYPE_TEXTURE_BUFFER: { - if (uniform.ids.size() != set_uniform.length) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") texture buffer elements, so it should be provided equal number of texture buffer IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") texture buffer elements, so it should be provided equal number of texture buffer IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture buffer (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture buffer (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorBufferInfo> buffer_info; Vector<VkBufferView> buffer_view; - for (int j = 0; j < uniform.ids.size(); j++) { - TextureBuffer *buffer = texture_buffer_owner.getornull(uniform.ids[j]); + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j)); ERR_FAIL_COND_V_MSG(!buffer, RID(), "Texture Buffer (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture buffer."); buffer_info.push_back(buffer->buffer.buffer_info); @@ -5064,21 +5843,21 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size(); + write.descriptorCount = uniform.get_id_count(); write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; write.pImageInfo = nullptr; write.pBufferInfo = buffer_infos.push_back(buffer_info)->get().ptr(); write.pTexelBufferView = buffer_views.push_back(buffer_view)->get().ptr(); - type_size = uniform.ids.size(); + type_size = uniform.get_id_count(); } break; case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: { - if (uniform.ids.size() != set_uniform.length * 2) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length * 2) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler buffer elements, so it should provided twice the amount of IDs (sampler,buffer pairs) to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler buffer elements, so it should provided twice the amount of IDs (sampler,buffer pairs) to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture buffer (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture buffer (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } @@ -5086,11 +5865,11 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, Vector<VkDescriptorBufferInfo> buffer_info; Vector<VkBufferView> buffer_view; - for (int j = 0; j < uniform.ids.size(); j += 2) { - VkSampler *sampler = sampler_owner.getornull(uniform.ids[j + 0]); + for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { + VkSampler *sampler = sampler_owner.get_or_null(uniform.get_id(j + 0)); ERR_FAIL_COND_V_MSG(!sampler, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); - TextureBuffer *buffer = texture_buffer_owner.getornull(uniform.ids[j + 1]); + TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j + 1)); VkDescriptorImageInfo img_info; img_info.sampler = *sampler; @@ -5106,23 +5885,23 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size() / 2; + write.descriptorCount = uniform.get_id_count() / 2; write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = buffer_infos.push_back(buffer_info)->get().ptr(); write.pTexelBufferView = buffer_views.push_back(buffer_view)->get().ptr(); - type_size = uniform.ids.size() / 2; + type_size = uniform.get_id_count() / 2; } break; case UNIFORM_TYPE_IMAGE_BUFFER: { //todo } break; case UNIFORM_TYPE_UNIFORM_BUFFER: { - ERR_FAIL_COND_V_MSG(uniform.ids.size() != 1, RID(), - "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.ids.size()) + " provided)."); + ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(), + "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.get_id_count()) + " provided)."); - Buffer *buffer = uniform_buffer_owner.getornull(uniform.ids[0]); + Buffer *buffer = uniform_buffer_owner.get_or_null(uniform.get_id(0)); ERR_FAIL_COND_V_MSG(!buffer, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); ERR_FAIL_COND_V_MSG(buffer->size != (uint32_t)set_uniform.length, RID(), @@ -5137,21 +5916,21 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } break; case UNIFORM_TYPE_STORAGE_BUFFER: { - ERR_FAIL_COND_V_MSG(uniform.ids.size() != 1, RID(), - "Storage buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.ids.size()) + " provided)."); + ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(), + "Storage buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.get_id_count()) + " provided)."); Buffer *buffer = nullptr; - if (storage_buffer_owner.owns(uniform.ids[0])) { - buffer = storage_buffer_owner.getornull(uniform.ids[0]); - } else if (vertex_buffer_owner.owns(uniform.ids[0])) { - buffer = vertex_buffer_owner.getornull(uniform.ids[0]); + if (storage_buffer_owner.owns(uniform.get_id(0))) { + buffer = storage_buffer_owner.get_or_null(uniform.get_id(0)); + } else if (vertex_buffer_owner.owns(uniform.get_id(0))) { + buffer = vertex_buffer_owner.get_or_null(uniform.get_id(0)); ERR_FAIL_COND_V_MSG(!(buffer->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), RID(), "Vertex buffer supplied (binding: " + itos(uniform.binding) + ") was not created with storage flag."); } ERR_FAIL_COND_V_MSG(!buffer, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); - //if 0, then its sized on link time + //if 0, then it's sized on link time ERR_FAIL_COND_V_MSG(set_uniform.length > 0 && buffer->size != (uint32_t)set_uniform.length, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " does not match size of shader uniform: (" + itos(set_uniform.length) + ")."); @@ -5163,6 +5942,49 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, write.pTexelBufferView = nullptr; } break; case UNIFORM_TYPE_INPUT_ATTACHMENT: { + ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") supplied for compute shader (this is not allowed)."); + + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); + } + } + + Vector<VkDescriptorImageInfo> image_info; + + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); + + ERR_FAIL_COND_V_MSG(!texture, RID(), + "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), + "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform."); + + VkDescriptorImageInfo img_info; + img_info.sampler = VK_NULL_HANDLE; + img_info.imageView = texture->view; + + if (texture->owner.is_valid()) { + texture = texture_owner.get_or_null(texture->owner); + ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + } + + img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + image_info.push_back(img_info); + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.get_id_count(); + write.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); + write.pBufferInfo = nullptr; + write.pTexelBufferView = nullptr; + + type_size = uniform.get_id_count(); } break; default: { } @@ -5212,10 +6034,9 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, _add_dependency(id, p_shader); for (uint32_t i = 0; i < uniform_count; i++) { const Uniform &uniform = uniforms[i]; - int id_count = uniform.ids.size(); - const RID *ids = uniform.ids.ptr(); + int id_count = uniform.get_id_count(); for (int j = 0; j < id_count; j++) { - _add_dependency(id, ids[j]); + _add_dependency(id, uniform.get_id(j)); } } @@ -5234,6 +6055,13 @@ bool RenderingDeviceVulkan::uniform_set_is_valid(RID p_uniform_set) { return uniform_set_owner.owns(p_uniform_set); } +void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata) { + UniformSet *us = uniform_set_owner.get_or_null(p_uniform_set); + ERR_FAIL_COND(!us); + us->invalidated_callback = p_callback; + us->invalidated_callback_userdata = p_userdata; +} + Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier) { _THREAD_SAFE_METHOD_ @@ -5336,7 +6164,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { ERR_FAIL_V_MSG(Vector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); } - // Make sure no one is using the buffer -- the "false" gets us to the same command buffer as below. + // Make sure no one is using the buffer -- the "false" gets us to the same command buffer as below. _buffer_memory_barrier(buffer->buffer, 0, buffer->size, src_stage_mask, src_access_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, false); VkCommandBuffer command_buffer = frames[frame].setup_command_buffer; @@ -5359,7 +6187,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { { buffer_data.resize(buffer->size); uint8_t *w = buffer_data.ptrw(); - copymem(w, buffer_mem, buffer->size); + memcpy(w, buffer_mem, buffer->size); } vmaUnmapMemory(allocator, tmp_buffer.allocation); @@ -5373,11 +6201,11 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { /**** RENDER PIPELINE ****/ /*************************/ -RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags) { +RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { _THREAD_SAFE_METHOD_ //needs a shader - Shader *shader = shader_owner.getornull(p_shader); + Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RID()); ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), @@ -5392,8 +6220,16 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma { //validate shader vs framebuffer - ERR_FAIL_COND_V_MSG(shader->fragment_outputs != fb_format.color_attachments, RID(), - "Mismatch fragment output bindings (" + itos(shader->fragment_outputs) + ") and framebuffer color buffers (" + itos(fb_format.color_attachments) + ") when binding both in render pipeline."); + ERR_FAIL_COND_V_MSG(p_for_render_pass >= uint32_t(fb_format.E->key().passes.size()), RID(), "Render pass requested for pipeline creation (" + itos(p_for_render_pass) + ") is out of bounds"); + const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; + uint32_t output_mask = 0; + for (int i = 0; i < pass.color_attachments.size(); i++) { + if (pass.color_attachments[i] != FramebufferPass::ATTACHMENT_UNUSED) { + output_mask |= 1 << i; + } + } + ERR_FAIL_COND_V_MSG(shader->fragment_output_mask != output_mask, RID(), + "Mismatch fragment shader output mask (" + itos(shader->fragment_output_mask) + ") and framebuffer color output mask (" + itos(output_mask) + ") when binding both in render pipeline."); } //vertex VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info; @@ -5465,7 +6301,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma tessellation_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; tessellation_create_info.pNext = nullptr; tessellation_create_info.flags = 0; - ERR_FAIL_COND_V(p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > limits.maxTessellationPatchSize, RID()); + ERR_FAIL_COND_V(limits.maxTessellationPatchSize > 0 && (p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > limits.maxTessellationPatchSize), RID()); tessellation_create_info.patchControlPoints = p_rasterization_state.patch_control_points; VkPipelineViewportStateCreateInfo viewport_state_create_info; @@ -5578,44 +6414,53 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma ERR_FAIL_INDEX_V(p_blend_state.logic_op, LOGIC_OP_MAX, RID()); color_blend_state_create_info.logicOp = logic_operations[p_blend_state.logic_op]; - ERR_FAIL_COND_V(fb_format.color_attachments != p_blend_state.attachments.size(), RID()); - Vector<VkPipelineColorBlendAttachmentState> attachment_states; + { + const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; + + for (int i = 0; i < pass.color_attachments.size(); i++) { + if (pass.color_attachments[i] != FramebufferPass::ATTACHMENT_UNUSED) { + int idx = attachment_states.size(); + + ERR_FAIL_INDEX_V(idx, p_blend_state.attachments.size(), RID()); + VkPipelineColorBlendAttachmentState state; + state.blendEnable = p_blend_state.attachments[idx].enable_blend; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[idx].src_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[idx].dst_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].color_blend_op, BLEND_OP_MAX, RID()); + state.colorBlendOp = blend_operations[p_blend_state.attachments[idx].color_blend_op]; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[idx].src_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[idx].dst_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].alpha_blend_op, BLEND_OP_MAX, RID()); + state.alphaBlendOp = blend_operations[p_blend_state.attachments[idx].alpha_blend_op]; + + state.colorWriteMask = 0; + if (p_blend_state.attachments[idx].write_r) { + state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; + } + if (p_blend_state.attachments[idx].write_g) { + state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; + } + if (p_blend_state.attachments[idx].write_b) { + state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; + } + if (p_blend_state.attachments[idx].write_a) { + state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; + } - for (int i = 0; i < p_blend_state.attachments.size(); i++) { - VkPipelineColorBlendAttachmentState state; - state.blendEnable = p_blend_state.attachments[i].enable_blend; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[i].src_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[i].dst_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].color_blend_op, BLEND_OP_MAX, RID()); - state.colorBlendOp = blend_operations[p_blend_state.attachments[i].color_blend_op]; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].src_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].dst_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[i].alpha_blend_op, BLEND_OP_MAX, RID()); - state.alphaBlendOp = blend_operations[p_blend_state.attachments[i].alpha_blend_op]; - - state.colorWriteMask = 0; - if (p_blend_state.attachments[i].write_r) { - state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; - } - if (p_blend_state.attachments[i].write_g) { - state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; - } - if (p_blend_state.attachments[i].write_b) { - state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; - } - if (p_blend_state.attachments[i].write_a) { - state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; + attachment_states.push_back(state); + idx++; + } } - attachment_states.push_back(state); - }; + ERR_FAIL_COND_V(attachment_states.size() != p_blend_state.attachments.size(), RID()); + } color_blend_state_create_info.attachmentCount = attachment_states.size(); color_blend_state_create_info.pAttachments = attachment_states.ptr(); @@ -5674,8 +6519,62 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma graphics_pipeline_create_info.pNext = nullptr; graphics_pipeline_create_info.flags = 0; - graphics_pipeline_create_info.stageCount = shader->pipeline_stages.size(); - graphics_pipeline_create_info.pStages = shader->pipeline_stages.ptr(); + Vector<VkPipelineShaderStageCreateInfo> pipeline_stages = shader->pipeline_stages; + Vector<VkSpecializationInfo> specialization_info; + Vector<Vector<VkSpecializationMapEntry>> specialization_map_entries; + Vector<uint32_t> specialization_constant_data; + + if (shader->specialization_constants.size()) { + specialization_constant_data.resize(shader->specialization_constants.size()); + uint32_t *data_ptr = specialization_constant_data.ptrw(); + specialization_info.resize(pipeline_stages.size()); + specialization_map_entries.resize(pipeline_stages.size()); + for (int i = 0; i < shader->specialization_constants.size(); i++) { + //see if overridden + const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; + data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + + for (int j = 0; j < p_specialization_constants.size(); j++) { + const PipelineSpecializationConstant &psc = p_specialization_constants[j]; + if (psc.constant_id == sc.constant.constant_id) { + ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, RID(), "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); + data_ptr[i] = psc.int_value; + break; + } + } + + VkSpecializationMapEntry entry; + + entry.constantID = sc.constant.constant_id; + entry.offset = i * sizeof(uint32_t); + entry.size = sizeof(uint32_t); + + for (int j = 0; j < SHADER_STAGE_MAX; j++) { + if (sc.stage_flags & (1 << j)) { + VkShaderStageFlagBits stage = shader_stage_masks[j]; + for (int k = 0; k < pipeline_stages.size(); k++) { + if (pipeline_stages[k].stage == stage) { + specialization_map_entries.write[k].push_back(entry); + } + } + } + } + } + + for (int i = 0; i < pipeline_stages.size(); i++) { + if (specialization_map_entries[i].size()) { + specialization_info.write[i].dataSize = specialization_constant_data.size() * sizeof(uint32_t); + specialization_info.write[i].pData = data_ptr; + specialization_info.write[i].mapEntryCount = specialization_map_entries[i].size(); + specialization_info.write[i].pMapEntries = specialization_map_entries[i].ptr(); + pipeline_stages.write[i].pSpecializationInfo = specialization_info.ptr() + i; + } + } + } + + graphics_pipeline_create_info.stageCount = pipeline_stages.size(); + graphics_pipeline_create_info.pStages = pipeline_stages.ptr(); + graphics_pipeline_create_info.pVertexInputState = &pipeline_vertex_input_state_create_info; graphics_pipeline_create_info.pInputAssemblyState = &input_assembly_create_info; graphics_pipeline_create_info.pTessellationState = &tessellation_create_info; @@ -5688,13 +6587,13 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma graphics_pipeline_create_info.layout = shader->pipeline_layout; graphics_pipeline_create_info.renderPass = fb_format.render_pass; - graphics_pipeline_create_info.subpass = 0; + graphics_pipeline_create_info.subpass = p_for_render_pass; graphics_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; graphics_pipeline_create_info.basePipelineIndex = 0; RenderPipeline pipeline; VkResult err = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline.pipeline); - ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateGraphicsPipelines failed with error " + itos(err) + "."); + ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateGraphicsPipelines failed with error " + itos(err) + " for shader '" + shader->name + "'."); pipeline.set_formats = shader->set_formats; pipeline.push_constant_stages = shader->push_constant.push_constants_vk_stage; @@ -5705,6 +6604,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma #ifdef DEBUG_ENABLED pipeline.validation.dynamic_state = p_dynamic_state_flags; pipeline.validation.framebuffer_format = p_framebuffer_format; + pipeline.validation.render_pass = p_for_render_pass; pipeline.validation.vertex_format = p_vertex_format; pipeline.validation.uses_restart_indices = input_assembly_create_info.primitiveRestartEnable; @@ -5729,7 +6629,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma #endif //create ID to associate with this pipeline RID id = render_pipeline_owner.make_rid(pipeline); - //now add aall the dependencies + //now add all the dependencies _add_dependency(id, p_shader); return id; } @@ -5743,11 +6643,11 @@ bool RenderingDeviceVulkan::render_pipeline_is_valid(RID p_pipeline) { /**** COMPUTE PIPELINE ****/ /**************************/ -RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { +RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { _THREAD_SAFE_METHOD_ //needs a shader - Shader *shader = shader_owner.getornull(p_shader); + Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RID()); ERR_FAIL_COND_V_MSG(!shader->is_compute, RID(), @@ -5765,6 +6665,44 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { compute_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; compute_pipeline_create_info.basePipelineIndex = 0; + VkSpecializationInfo specialization_info; + Vector<VkSpecializationMapEntry> specialization_map_entries; + Vector<uint32_t> specialization_constant_data; + + if (shader->specialization_constants.size()) { + specialization_constant_data.resize(shader->specialization_constants.size()); + uint32_t *data_ptr = specialization_constant_data.ptrw(); + for (int i = 0; i < shader->specialization_constants.size(); i++) { + //see if overridden + const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; + data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + + for (int j = 0; j < p_specialization_constants.size(); j++) { + const PipelineSpecializationConstant &psc = p_specialization_constants[j]; + if (psc.constant_id == sc.constant.constant_id) { + ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, RID(), "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); + data_ptr[i] = sc.constant.int_value; + break; + } + } + + VkSpecializationMapEntry entry; + + entry.constantID = sc.constant.constant_id; + entry.offset = i * sizeof(uint32_t); + entry.size = sizeof(uint32_t); + + specialization_map_entries.push_back(entry); + } + + specialization_info.dataSize = specialization_constant_data.size() * sizeof(uint32_t); + specialization_info.pData = data_ptr; + specialization_info.mapEntryCount = specialization_map_entries.size(); + specialization_info.pMapEntries = specialization_map_entries.ptr(); + + compute_pipeline_create_info.stage.pSpecializationInfo = &specialization_info; + } + ComputePipeline pipeline; VkResult err = vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &compute_pipeline_create_info, nullptr, &pipeline.pipeline); ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateComputePipelines failed with error " + itos(err) + "."); @@ -5780,7 +6718,7 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { //create ID to associate with this pipeline RID id = compute_pipeline_owner.make_rid(pipeline); - //now add aall the dependencies + //now add all the dependencies _add_dependency(id, p_shader); return id; } @@ -5843,13 +6781,18 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - draw_list = memnew(DrawList); - draw_list->command_buffer = command_buffer; + + if (!context->window_is_valid_swapchain(p_screen)) { + return INVALID_ID; + } + + Size2i size = Size2i(context->window_get_width(p_screen), context->window_get_height(p_screen)); + + _draw_list_allocate(Rect2i(Vector2i(), size), 0, 0); #ifdef DEBUG_ENABLED - draw_list->validation.framebuffer_format = screen_get_framebuffer_format(); + draw_list_framebuffer_format = screen_get_framebuffer_format(); #endif - draw_list_count = 0; - draw_list_split = false; + draw_list_subpass_count = 1; VkRenderPassBeginInfo render_pass_begin; render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; @@ -5857,8 +6800,8 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di render_pass_begin.renderPass = context->window_get_render_pass(p_screen); render_pass_begin.framebuffer = context->window_get_framebuffer(p_screen); - render_pass_begin.renderArea.extent.width = context->window_get_width(p_screen); - render_pass_begin.renderArea.extent.height = context->window_get_height(p_screen); + render_pass_begin.renderArea.extent.width = size.width; + render_pass_begin.renderArea.extent.height = size.height; render_pass_begin.renderArea.offset.x = 0; render_pass_begin.renderArea.offset.y = 0; @@ -5898,18 +6841,19 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } -Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass) { +Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count) { Framebuffer::VersionKey vk; vk.initial_color_action = p_initial_color_action; vk.final_color_action = p_final_color_action; vk.initial_depth_action = p_initial_depth_action; vk.final_depth_action = p_final_depth_action; + vk.view_count = p_framebuffer->view_count; if (!p_framebuffer->framebuffers.has(vk)) { //need to create this version Framebuffer::Version version; - version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action); + version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, framebuffer_formats[p_framebuffer->format_id].E->key().passes, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_framebuffer->view_count); VkFramebufferCreateInfo framebuffer_create_info; framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; @@ -5918,7 +6862,7 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu framebuffer_create_info.renderPass = version.render_pass; Vector<VkImageView> attachments; for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.getornull(p_framebuffer->texture_ids[i]); + Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); ERR_FAIL_COND_V(!texture, ERR_BUG); attachments.push_back(texture->view); ERR_FAIL_COND_V(texture->width != p_framebuffer->size.width, ERR_BUG); @@ -5933,11 +6877,14 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu VkResult err = vkCreateFramebuffer(device, &framebuffer_create_info, nullptr, &version.framebuffer); ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkCreateFramebuffer failed with error " + itos(err) + "."); + version.subpass_count = framebuffer_formats[p_framebuffer->format_id].E->key().passes.size(); + p_framebuffer->framebuffers.insert(vk, version); } const Framebuffer::Version &version = p_framebuffer->framebuffers[vk]; *r_framebuffer = version.framebuffer; *r_render_pass = version.render_pass; + *r_subpass_count = version.subpass_count; return OK; } @@ -5967,7 +6914,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff { int color_index = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); VkClearValue clear_value; if (color_index < p_clear_colors.size() && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { @@ -5995,7 +6942,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff render_pass_begin.pClearValues = clear_values.ptr(); for (int i = 0; i < p_storage_textures.size(); i++) { - Texture *texture = texture_owner.getornull(p_storage_textures[i]); + Texture *texture = texture_owner.get_or_null(p_storage_textures[i]); ERR_CONTINUE_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), "Supplied storage texture " + itos(i) + " for draw list is not set to be used for storage."); if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { @@ -6033,7 +6980,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff draw_list_unbind_depth_textures = p_final_depth_action != FINAL_ACTION_CONTINUE; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); texture->bound = true; draw_list_bound_textures.push_back(framebuffer->texture_ids[i]); } @@ -6045,7 +6992,7 @@ void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *draw_list, Vector<VkClearAttachment> clear_attachments; int color_index = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]); + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); VkClearAttachment clear_at = {}; if (p_clear_color && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { @@ -6088,7 +7035,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); ERR_FAIL_COND_V_MSG(compute_list != nullptr && !compute_list->state.allow_draw_overlap, INVALID_ID, "Only one draw/compute list can be active at the same time."); - Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer); + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); ERR_FAIL_COND_V(!framebuffer, INVALID_ID); Point2i viewport_offset; @@ -6127,15 +7074,23 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values - int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, INVALID_ID, - "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ")."); + int color_count = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + + if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + color_count++; + } + } + + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, INVALID_ID, + "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(color_count) + ")."); } VkFramebuffer vkframebuffer; VkRenderPass render_pass; - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass); + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count); ERR_FAIL_COND_V(err != OK, INVALID_ID); VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; @@ -6145,13 +7100,14 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu return INVALID_ID; } - draw_list = memnew(DrawList); - draw_list->command_buffer = command_buffer; + draw_list_render_pass = render_pass; + draw_list_vkframebuffer = vkframebuffer; + + _draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0, 0); #ifdef DEBUG_ENABLED - draw_list->validation.framebuffer_format = framebuffer->format_id; + draw_list_framebuffer_format = framebuffer->format_id; #endif - draw_list_count = 0; - draw_list_split = false; + draw_list_current_subpass = 0; if (needs_clear_color || needs_clear_depth) { _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); @@ -6175,7 +7131,6 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu vkCmdSetScissor(command_buffer, 0, 1, &scissor); - draw_list->viewport = Rect2i(viewport_offset, viewport_size); return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } @@ -6184,7 +7139,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p ERR_FAIL_COND_V(p_splits < 1, ERR_INVALID_DECLARATION); - Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer); + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); ERR_FAIL_COND_V(!framebuffer, ERR_INVALID_DECLARATION); Point2i viewport_offset; @@ -6217,47 +7172,23 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values - int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments; - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, ERR_INVALID_PARAMETER, - "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ")."); - } - - if (p_splits > (uint32_t)split_draw_list_allocators.size()) { - uint32_t from = split_draw_list_allocators.size(); - split_draw_list_allocators.resize(p_splits); - for (uint32_t i = from; i < p_splits; i++) { - VkCommandPoolCreateInfo cmd_pool_info; - cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmd_pool_info.pNext = nullptr; - cmd_pool_info.queueFamilyIndex = context->get_graphics_queue(); - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - - VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool); - ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "vkCreateCommandPool failed with error " + itos(res) + "."); - - for (int j = 0; j < frame_count; j++) { - VkCommandBuffer command_buffer; - - VkCommandBufferAllocateInfo cmdbuf; - //no command buffer exists, create it. - cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmdbuf.pNext = nullptr; - cmdbuf.commandPool = split_draw_list_allocators[i].command_pool; - cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - cmdbuf.commandBufferCount = 1; - - VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkAllocateCommandBuffers failed with error " + itos(err) + "."); + int color_count = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer); + if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + color_count++; } } + + ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, ERR_INVALID_PARAMETER, + "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_count) + ")."); } VkFramebuffer vkframebuffer; VkRenderPass render_pass; - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass); + Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); VkCommandBuffer frame_command_buffer = frames[frame].draw_command_buffer; @@ -6267,53 +7198,24 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p return ERR_CANT_CREATE; } - draw_list = memnew_arr(DrawList, p_splits); - draw_list_count = p_splits; - draw_list_split = true; - - for (uint32_t i = 0; i < p_splits; i++) { - //take a command buffer and initialize it - VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame]; - - VkCommandBufferInheritanceInfo inheritance_info; - inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; - inheritance_info.pNext = nullptr; - inheritance_info.renderPass = render_pass; - inheritance_info.subpass = 0; - inheritance_info.framebuffer = vkframebuffer; - inheritance_info.occlusionQueryEnable = false; - inheritance_info.queryFlags = 0; //? - inheritance_info.pipelineStatistics = 0; - - VkCommandBufferBeginInfo cmdbuf_begin; - cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmdbuf_begin.pNext = nullptr; - cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; - cmdbuf_begin.pInheritanceInfo = &inheritance_info; + draw_list_current_subpass = 0; - VkResult res = vkResetCommandBuffer(command_buffer, 0); - if (res) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkResetCommandBuffer failed with error " + itos(res) + "."); - } - - res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin); - if (res) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkBeginCommandBuffer failed with error " + itos(res) + "."); - } - - draw_list[i].command_buffer = command_buffer; #ifdef DEBUG_ENABLED - draw_list[i].validation.framebuffer_format = framebuffer->format_id; + draw_list_framebuffer_format = framebuffer->format_id; #endif + draw_list_render_pass = render_pass; + draw_list_vkframebuffer = vkframebuffer; - if (i == 0 && (needs_clear_color || needs_clear_depth)) { - _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); - } + err = _draw_list_allocate(Rect2i(viewport_offset, viewport_size), p_splits, 0); + if (err != OK) { + return err; + } + if (needs_clear_color || needs_clear_depth) { + _draw_list_insert_clear_region(&draw_list[0], framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); + } + + for (uint32_t i = 0; i < p_splits; i++) { VkViewport viewport; viewport.x = viewport_offset.x; viewport.y = viewport_offset.y; @@ -6322,7 +7224,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p viewport.minDepth = 0; viewport.maxDepth = 1.0; - vkCmdSetViewport(command_buffer, 0, 1, &viewport); + vkCmdSetViewport(draw_list[i].command_buffer, 0, 1, &viewport); VkRect2D scissor; scissor.offset.x = viewport_offset.x; @@ -6330,10 +7232,8 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p scissor.extent.width = viewport_size.width; scissor.extent.height = viewport_size.height; - vkCmdSetScissor(command_buffer, 0, 1, &scissor); + vkCmdSetScissor(draw_list[i].command_buffer, 0, 1, &scissor); r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; - - draw_list[i].viewport = Rect2i(viewport_offset, viewport_size); } return OK; @@ -6375,10 +7275,10 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); #endif - const RenderPipeline *pipeline = render_pipeline_owner.getornull(p_render_pipeline); + const RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_render_pipeline); ERR_FAIL_COND(!pipeline); #ifdef DEBUG_ENABLED - ERR_FAIL_COND(pipeline->validation.framebuffer_format != dl->validation.framebuffer_format); + ERR_FAIL_COND(pipeline->validation.framebuffer_format != draw_list_framebuffer_format && pipeline->validation.render_pass != draw_list_current_subpass); #endif if (p_render_pipeline == dl->state.pipeline) { @@ -6450,7 +7350,7 @@ void RenderingDeviceVulkan::draw_list_bind_uniform_set(DrawListID p_list, RID p_ ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); #endif - const UniformSet *uniform_set = uniform_set_owner.getornull(p_uniform_set); + const UniformSet *uniform_set = uniform_set_owner.get_or_null(p_uniform_set); ERR_FAIL_COND(!uniform_set); if (p_index > dl->state.set_count) { @@ -6498,7 +7398,7 @@ void RenderingDeviceVulkan::draw_list_bind_vertex_array(DrawListID p_list, RID p ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); #endif - const VertexArray *vertex_array = vertex_array_owner.getornull(p_vertex_array); + const VertexArray *vertex_array = vertex_array_owner.get_or_null(p_vertex_array); ERR_FAIL_COND(!vertex_array); if (dl->state.vertex_array == p_vertex_array) { @@ -6522,7 +7422,7 @@ void RenderingDeviceVulkan::draw_list_bind_index_array(DrawListID p_list, RID p_ ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); #endif - const IndexArray *index_array = index_array_owner.getornull(p_index_array); + const IndexArray *index_array = index_array_owner.get_or_null(p_index_array); ERR_FAIL_COND(!index_array); if (dl->state.index_array == p_index_array) { @@ -6608,7 +7508,7 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices if (dl->state.sets[i].uniform_set_format == 0) { ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); } else if (uniform_set_owner.owns(dl->state.sets[i].uniform_set)) { - UniformSet *us = uniform_set_owner.getornull(dl->state.sets[i].uniform_set); + UniformSet *us = uniform_set_owner.get_or_null(dl->state.sets[i].uniform_set); ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader)); } else { ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader)); @@ -6717,33 +7617,175 @@ void RenderingDeviceVulkan::draw_list_disable_scissor(DrawListID p_list) { vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor); } -void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { - _THREAD_SAFE_METHOD_ +uint32_t RenderingDeviceVulkan::draw_list_get_current_pass() { + return draw_list_current_subpass; +} - ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); +RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_switch_to_next_pass() { + ERR_FAIL_COND_V(draw_list == nullptr, INVALID_ID); + ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID); + + draw_list_current_subpass++; + + Rect2i viewport; + _draw_list_free(&viewport); + + vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE); + + _draw_list_allocate(viewport, 0, draw_list_current_subpass); + + return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; +} +Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) { + ERR_FAIL_COND_V(draw_list == nullptr, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, ERR_INVALID_PARAMETER); + + draw_list_current_subpass++; + + Rect2i viewport; + _draw_list_free(&viewport); + + vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE); + + _draw_list_allocate(viewport, p_splits, draw_list_current_subpass); + + for (uint32_t i = 0; i < p_splits; i++) { + r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; + } + + return OK; +} + +Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) { + // Lock while draw_list is active + _THREAD_SAFE_LOCK_ + + if (p_splits == 0) { + draw_list = memnew(DrawList); + draw_list->command_buffer = frames[frame].draw_command_buffer; + draw_list->viewport = p_viewport; + draw_list_count = 0; + draw_list_split = false; + } else { + if (p_splits > (uint32_t)split_draw_list_allocators.size()) { + uint32_t from = split_draw_list_allocators.size(); + split_draw_list_allocators.resize(p_splits); + for (uint32_t i = from; i < p_splits; i++) { + VkCommandPoolCreateInfo cmd_pool_info; + cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmd_pool_info.pNext = nullptr; + cmd_pool_info.queueFamilyIndex = context->get_graphics_queue_family_index(); + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool); + ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "vkCreateCommandPool failed with error " + itos(res) + "."); + + for (int j = 0; j < frame_count; j++) { + VkCommandBuffer command_buffer; + + VkCommandBufferAllocateInfo cmdbuf; + //no command buffer exists, create it. + cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdbuf.pNext = nullptr; + cmdbuf.commandPool = split_draw_list_allocators[i].command_pool; + cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + cmdbuf.commandBufferCount = 1; + + VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkAllocateCommandBuffers failed with error " + itos(err) + "."); + + split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer); + } + } + } + draw_list = memnew_arr(DrawList, p_splits); + draw_list_count = p_splits; + draw_list_split = true; + + for (uint32_t i = 0; i < p_splits; i++) { + //take a command buffer and initialize it + VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame]; + + VkCommandBufferInheritanceInfo inheritance_info; + inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; + inheritance_info.pNext = nullptr; + inheritance_info.renderPass = draw_list_render_pass; + inheritance_info.subpass = p_subpass; + inheritance_info.framebuffer = draw_list_vkframebuffer; + inheritance_info.occlusionQueryEnable = false; + inheritance_info.queryFlags = 0; //? + inheritance_info.pipelineStatistics = 0; + + VkCommandBufferBeginInfo cmdbuf_begin; + cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdbuf_begin.pNext = nullptr; + cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + cmdbuf_begin.pInheritanceInfo = &inheritance_info; + + VkResult res = vkResetCommandBuffer(command_buffer, 0); + if (res) { + memdelete_arr(draw_list); + draw_list = nullptr; + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkResetCommandBuffer failed with error " + itos(res) + "."); + } + + res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin); + if (res) { + memdelete_arr(draw_list); + draw_list = nullptr; + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkBeginCommandBuffer failed with error " + itos(res) + "."); + } + + draw_list[i].command_buffer = command_buffer; + draw_list[i].viewport = p_viewport; + } + } + return OK; +} + +void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) { if (draw_list_split) { //send all command buffers VkCommandBuffer *command_buffers = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer) * draw_list_count); for (uint32_t i = 0; i < draw_list_count; i++) { vkEndCommandBuffer(draw_list[i].command_buffer); command_buffers[i] = draw_list[i].command_buffer; + if (r_last_viewport) { + if (i == 0 || draw_list[i].viewport_set) { + *r_last_viewport = draw_list[i].viewport; + } + } } vkCmdExecuteCommands(frames[frame].draw_command_buffer, draw_list_count, command_buffers); - vkCmdEndRenderPass(frames[frame].draw_command_buffer); memdelete_arr(draw_list); draw_list = nullptr; } else { + if (r_last_viewport) { + *r_last_viewport = draw_list->viewport; + } //just end the list - vkCmdEndRenderPass(draw_list->command_buffer); memdelete(draw_list); draw_list = nullptr; } + // draw_list is no longer active + _THREAD_SAFE_UNLOCK_ +} + +void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); + + _draw_list_free(); + + vkCmdEndRenderPass(frames[frame].draw_command_buffer); + for (int i = 0; i < draw_list_bound_textures.size(); i++) { - Texture *texture = texture_owner.getornull(draw_list_bound_textures[i]); + Texture *texture = texture_owner.get_or_null(draw_list_bound_textures[i]); ERR_CONTINUE(!texture); //wtf if (draw_list_unbind_color_textures && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { texture->bound = false; @@ -6791,7 +7833,7 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { } for (uint32_t i = 0; i < image_barrier_count; i++) { - Texture *texture = texture_owner.getornull(draw_list_storage_textures[i]); + Texture *texture = texture_owner.get_or_null(draw_list_storage_textures[i]); VkImageMemoryBarrier &image_memory_barrier = image_barriers[i]; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -6817,7 +7859,7 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { // To ensure proper synchronization, we must make sure rendering is done before: // * Some buffer is copied - // * Another render pass happens (since we may be done + // * Another render pass happens (since we may be done) #ifdef FORCE_FULL_BARRIER _full_barrier(true); @@ -6844,6 +7886,9 @@ RenderingDevice::ComputeListID RenderingDeviceVulkan::compute_list_begin(bool p_ ERR_FAIL_COND_V_MSG(!p_allow_draw_overlap && draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); + // Lock while compute_list is active + _THREAD_SAFE_LOCK_ + compute_list = memnew(ComputeList); compute_list->command_buffer = frames[frame].draw_command_buffer; compute_list->state.allow_draw_overlap = p_allow_draw_overlap; @@ -6857,7 +7902,7 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l ComputeList *cl = compute_list; - const ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_compute_pipeline); + const ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_compute_pipeline); ERR_FAIL_COND(!pipeline); if (p_compute_pipeline == cl->state.pipeline) { @@ -6930,7 +7975,7 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); #endif - UniformSet *uniform_set = uniform_set_owner.getornull(p_uniform_set); + UniformSet *uniform_set = uniform_set_owner.get_or_null(p_uniform_set); ERR_FAIL_COND(!uniform_set); if (p_index > cl->state.set_count) { @@ -7012,13 +8057,13 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, textures_to_storage[i]->used_in_compute = false; textures_to_storage[i]->used_in_raster = false; - textures_to_storage[i]->used_in_compute = false; + textures_to_storage[i]->used_in_transfer = false; } else { src_access_flags = 0; textures_to_storage[i]->used_in_compute = false; textures_to_storage[i]->used_in_raster = false; - textures_to_storage[i]->used_in_compute = false; + textures_to_storage[i]->used_in_transfer = false; textures_to_storage[i]->used_in_frame = frames_drawn; } @@ -7096,12 +8141,15 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t ComputeList *cl = compute_list; #ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_x_groups == 0, "Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is zero."); + ERR_FAIL_COND_MSG(p_z_groups == 0, "Dispatch amount of Z compute groups (" + itos(p_z_groups) + ") is zero."); + ERR_FAIL_COND_MSG(p_y_groups == 0, "Dispatch amount of Y compute groups (" + itos(p_y_groups) + ") is zero."); ERR_FAIL_COND_MSG(p_x_groups > limits.maxComputeWorkGroupCount[0], "Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")"); ERR_FAIL_COND_MSG(p_y_groups > limits.maxComputeWorkGroupCount[1], - "Dispatch amount of Y compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")"); + "Dispatch amount of Y compute groups (" + itos(p_y_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[1]) + ")"); ERR_FAIL_COND_MSG(p_z_groups > limits.maxComputeWorkGroupCount[2], - "Dispatch amount of Z compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")"); + "Dispatch amount of Z compute groups (" + itos(p_z_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[2]) + ")"); ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); #endif @@ -7129,7 +8177,7 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t if (cl->state.sets[i].uniform_set_format == 0) { ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); } else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) { - UniformSet *us = uniform_set_owner.getornull(cl->state.sets[i].uniform_set); + UniformSet *us = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); } else { ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); @@ -7150,6 +8198,12 @@ void RenderingDeviceVulkan::compute_list_dispatch_threads(ComputeListID p_list, ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); ERR_FAIL_COND(!compute_list); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_x_threads == 0, "Dispatch amount of X compute threads (" + itos(p_x_threads) + ") is zero."); + ERR_FAIL_COND_MSG(p_y_threads == 0, "Dispatch amount of Y compute threads (" + itos(p_y_threads) + ") is zero."); + ERR_FAIL_COND_MSG(p_z_threads == 0, "Dispatch amount of Z compute threads (" + itos(p_z_threads) + ") is zero."); +#endif + ComputeList *cl = compute_list; #ifdef DEBUG_ENABLED @@ -7172,7 +8226,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, ERR_FAIL_COND(!compute_list); ComputeList *cl = compute_list; - Buffer *buffer = storage_buffer_owner.getornull(p_buffer); + Buffer *buffer = storage_buffer_owner.get_or_null(p_buffer); ERR_FAIL_COND(!buffer); ERR_FAIL_COND_MSG(!(buffer->usage & STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT), "Buffer provided was not created to do indirect dispatch."); @@ -7206,7 +8260,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, if (cl->state.sets[i].uniform_set_format == 0) { ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); } else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) { - UniformSet *us = uniform_set_owner.getornull(cl->state.sets[i].uniform_set); + UniformSet *us = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); } else { ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); @@ -7308,6 +8362,9 @@ void RenderingDeviceVulkan::compute_list_end(uint32_t p_post_barrier) { memdelete(compute_list); compute_list = nullptr; + + // compute_list is no longer active + _THREAD_SAFE_UNLOCK_ } void RenderingDeviceVulkan::barrier(uint32_t p_from, uint32_t p_to) { @@ -7418,60 +8475,65 @@ void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_frame void RenderingDeviceVulkan::_free_internal(RID p_id) { //push everything so it's disposed of next time this frame index is processed (means, it's safe to do it) if (texture_owner.owns(p_id)) { - Texture *texture = texture_owner.getornull(p_id); + Texture *texture = texture_owner.get_or_null(p_id); frames[frame].textures_to_dispose_of.push_back(*texture); texture_owner.free(p_id); } else if (framebuffer_owner.owns(p_id)) { - Framebuffer *framebuffer = framebuffer_owner.getornull(p_id); + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); frames[frame].framebuffers_to_dispose_of.push_back(*framebuffer); framebuffer_owner.free(p_id); } else if (sampler_owner.owns(p_id)) { - VkSampler *sampler = sampler_owner.getornull(p_id); + VkSampler *sampler = sampler_owner.get_or_null(p_id); frames[frame].samplers_to_dispose_of.push_back(*sampler); sampler_owner.free(p_id); } else if (vertex_buffer_owner.owns(p_id)) { - Buffer *vertex_buffer = vertex_buffer_owner.getornull(p_id); + Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id); frames[frame].buffers_to_dispose_of.push_back(*vertex_buffer); vertex_buffer_owner.free(p_id); } else if (vertex_array_owner.owns(p_id)) { vertex_array_owner.free(p_id); } else if (index_buffer_owner.owns(p_id)) { - IndexBuffer *index_buffer = index_buffer_owner.getornull(p_id); + IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id); Buffer b; b.allocation = index_buffer->allocation; b.buffer = index_buffer->buffer; b.size = index_buffer->size; + b.buffer_info = {}; frames[frame].buffers_to_dispose_of.push_back(b); index_buffer_owner.free(p_id); } else if (index_array_owner.owns(p_id)) { index_array_owner.free(p_id); } else if (shader_owner.owns(p_id)) { - Shader *shader = shader_owner.getornull(p_id); + Shader *shader = shader_owner.get_or_null(p_id); frames[frame].shaders_to_dispose_of.push_back(*shader); shader_owner.free(p_id); } else if (uniform_buffer_owner.owns(p_id)) { - Buffer *uniform_buffer = uniform_buffer_owner.getornull(p_id); + Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id); frames[frame].buffers_to_dispose_of.push_back(*uniform_buffer); uniform_buffer_owner.free(p_id); } else if (texture_buffer_owner.owns(p_id)) { - TextureBuffer *texture_buffer = texture_buffer_owner.getornull(p_id); + TextureBuffer *texture_buffer = texture_buffer_owner.get_or_null(p_id); frames[frame].buffers_to_dispose_of.push_back(texture_buffer->buffer); frames[frame].buffer_views_to_dispose_of.push_back(texture_buffer->view); texture_buffer_owner.free(p_id); } else if (storage_buffer_owner.owns(p_id)) { - Buffer *storage_buffer = storage_buffer_owner.getornull(p_id); + Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id); frames[frame].buffers_to_dispose_of.push_back(*storage_buffer); storage_buffer_owner.free(p_id); } else if (uniform_set_owner.owns(p_id)) { - UniformSet *uniform_set = uniform_set_owner.getornull(p_id); + UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id); frames[frame].uniform_sets_to_dispose_of.push_back(*uniform_set); uniform_set_owner.free(p_id); + + if (uniform_set->invalidated_callback != nullptr) { + uniform_set->invalidated_callback(uniform_set->invalidated_callback_userdata); + } } else if (render_pipeline_owner.owns(p_id)) { - RenderPipeline *pipeline = render_pipeline_owner.getornull(p_id); + RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id); frames[frame].render_pipelines_to_dispose_of.push_back(*pipeline); render_pipeline_owner.free(p_id); } else if (compute_pipeline_owner.owns(p_id)) { - ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_id); + ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_id); frames[frame].compute_pipelines_to_dispose_of.push_back(*pipeline); compute_pipeline_owner.free(p_id); } else { @@ -7490,49 +8552,49 @@ void RenderingDeviceVulkan::free(RID p_id) { // We just expose the resources that are owned and can be accessed easily. void RenderingDeviceVulkan::set_resource_name(RID p_id, const String p_name) { if (texture_owner.owns(p_id)) { - Texture *texture = texture_owner.getornull(p_id); + Texture *texture = texture_owner.get_or_null(p_id); if (texture->owner.is_null()) { // Don't set the source texture's name when calling on a texture view context->set_object_name(VK_OBJECT_TYPE_IMAGE, uint64_t(texture->image), p_name); } context->set_object_name(VK_OBJECT_TYPE_IMAGE_VIEW, uint64_t(texture->view), p_name + " View"); } else if (framebuffer_owner.owns(p_id)) { - //Framebuffer *framebuffer = framebuffer_owner.getornull(p_id); + //Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); // Not implemented for now as the relationship between Framebuffer and RenderPass is very complex } else if (sampler_owner.owns(p_id)) { - VkSampler *sampler = sampler_owner.getornull(p_id); + VkSampler *sampler = sampler_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_SAMPLER, uint64_t(*sampler), p_name); } else if (vertex_buffer_owner.owns(p_id)) { - Buffer *vertex_buffer = vertex_buffer_owner.getornull(p_id); + Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(vertex_buffer->buffer), p_name); } else if (index_buffer_owner.owns(p_id)) { - IndexBuffer *index_buffer = index_buffer_owner.getornull(p_id); + IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(index_buffer->buffer), p_name); } else if (shader_owner.owns(p_id)) { - Shader *shader = shader_owner.getornull(p_id); + Shader *shader = shader_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(shader->pipeline_layout), p_name + " Pipeline Layout"); for (int i = 0; i < shader->sets.size(); i++) { context->set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, uint64_t(shader->sets[i].descriptor_set_layout), p_name); } } else if (uniform_buffer_owner.owns(p_id)) { - Buffer *uniform_buffer = uniform_buffer_owner.getornull(p_id); + Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(uniform_buffer->buffer), p_name); } else if (texture_buffer_owner.owns(p_id)) { - TextureBuffer *texture_buffer = texture_buffer_owner.getornull(p_id); + TextureBuffer *texture_buffer = texture_buffer_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(texture_buffer->buffer.buffer), p_name); context->set_object_name(VK_OBJECT_TYPE_BUFFER_VIEW, uint64_t(texture_buffer->view), p_name + " View"); } else if (storage_buffer_owner.owns(p_id)) { - Buffer *storage_buffer = storage_buffer_owner.getornull(p_id); + Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(storage_buffer->buffer), p_name); } else if (uniform_set_owner.owns(p_id)) { - UniformSet *uniform_set = uniform_set_owner.getornull(p_id); + UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET, uint64_t(uniform_set->descriptor_set), p_name); } else if (render_pipeline_owner.owns(p_id)) { - RenderPipeline *pipeline = render_pipeline_owner.getornull(p_id); + RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_PIPELINE, uint64_t(pipeline->pipeline), p_name); context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(pipeline->pipeline_layout), p_name + " Layout"); } else if (compute_pipeline_owner.owns(p_id)) { - ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_id); + ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_PIPELINE, uint64_t(pipeline->pipeline), p_name); context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(pipeline->pipeline_layout), p_name + " Layout"); } else { @@ -7558,6 +8620,11 @@ String RenderingDeviceVulkan::get_device_vendor_name() const { String RenderingDeviceVulkan::get_device_name() const { return context->get_device_name(); } + +RenderingDevice::DeviceType RenderingDeviceVulkan::get_device_type() const { + return context->get_device_type(); +} + String RenderingDeviceVulkan::get_device_pipeline_cache_uuid() const { return context->get_device_pipeline_cache_uuid(); } @@ -7659,6 +8726,30 @@ void RenderingDeviceVulkan::sync() { local_device_processing = false; } +VmaPool RenderingDeviceVulkan::_find_or_create_small_allocs_pool(uint32_t p_mem_type_index) { + if (small_allocs_pools.has(p_mem_type_index)) { + return small_allocs_pools[p_mem_type_index]; + } + + print_verbose("Creating VMA small objects pool for memory type index " + itos(p_mem_type_index)); + + VmaPoolCreateInfo pci; + pci.memoryTypeIndex = p_mem_type_index; + pci.flags = 0; + pci.blockSize = 0; + pci.minBlockCount = 0; + pci.maxBlockCount = SIZE_MAX; + pci.priority = 0.5f; + pci.minAllocationAlignment = 0; + pci.pMemoryAllocateNext = nullptr; + VmaPool pool = VK_NULL_HANDLE; + VkResult res = vmaCreatePool(allocator, &pci, &pool); + small_allocs_pools[p_mem_type_index] = pool; // Don't try to create it again if failed the first time + ERR_FAIL_COND_V_MSG(res, pool, "vmaCreatePool failed with error " + itos(res) + "."); + + return pool; +} + void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { //free in dependency usage order, so nothing weird happens //pipelines @@ -7730,10 +8821,10 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { while (frames[p_frame].framebuffers_to_dispose_of.front()) { Framebuffer *framebuffer = &frames[p_frame].framebuffers_to_dispose_of.front()->get(); - for (Map<Framebuffer::VersionKey, Framebuffer::Version>::Element *E = framebuffer->framebuffers.front(); E; E = E->next()) { + for (const KeyValue<Framebuffer::VersionKey, Framebuffer::Version> &E : framebuffer->framebuffers) { //first framebuffer, then render pass because it depends on it - vkDestroyFramebuffer(device, E->get().framebuffer, nullptr); - vkDestroyRenderPass(device, E->get().render_pass, nullptr); + vkDestroyFramebuffer(device, E.value.framebuffer, nullptr); + vkDestroyRenderPass(device, E.value.render_pass, nullptr); } frames[p_frame].framebuffers_to_dispose_of.pop_front(); @@ -7749,6 +8840,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { vkDestroyImageView(device, texture->view, nullptr); if (texture->owner.is_null()) { //actually owns the image and the allocation too + image_memory -= texture->allocation_info.size; vmaDestroyImage(allocator, texture->image, texture->allocation); } frames[p_frame].textures_to_dispose_of.pop_front(); @@ -7772,15 +8864,21 @@ uint32_t RenderingDeviceVulkan::get_frame_delay() const { return frame_count; } -uint64_t RenderingDeviceVulkan::get_memory_usage() const { - VmaStats stats; - vmaCalculateStats(allocator, &stats); - return stats.total.usedBytes; +uint64_t RenderingDeviceVulkan::get_memory_usage(MemoryType p_type) const { + if (p_type == MEMORY_BUFFERS) { + return buffer_memory; + } else if (p_type == MEMORY_TEXTURES) { + return image_memory; + } else { + VmaTotalStatistics stats; + vmaCalculateStatistics(allocator, &stats); + return stats.total.statistics.allocationBytes; + } } void RenderingDeviceVulkan::_flush(bool p_current_frame) { if (local_device.is_valid() && !p_current_frame) { - return; //flushign previous frames has no effect with local device + return; //flushing previous frames has no effect with local device } //not doing this crashes RADV (undefined behavior) if (p_current_frame) { @@ -7834,6 +8932,23 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) { } void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_device) { + // get our device capabilities + { + device_capabilities.version_major = p_context->get_vulkan_major(); + device_capabilities.version_minor = p_context->get_vulkan_minor(); + + // get info about subgroups + VulkanContext::SubgroupCapabilities subgroup_capabilities = p_context->get_subgroup_capabilities(); + device_capabilities.subgroup_size = subgroup_capabilities.size; + device_capabilities.subgroup_in_shaders = subgroup_capabilities.supported_stages_flags_rd(); + device_capabilities.subgroup_operations = subgroup_capabilities.supported_operations_flags_rd(); + + // get info about further features + VulkanContext::MultiviewCapabilities multiview_capabilies = p_context->get_multiview_capabilities(); + device_capabilities.supports_multiview = multiview_capabilies.is_supported && multiview_capabilies.max_view_count > 1; + device_capabilities.supports_fsr_half_float = p_context->get_shader_capabilities().shader_float16_is_supported && p_context->get_storage_buffer_capabilities().storage_buffer_16_bit_access_is_supported; + } + context = p_context; device = p_context->get_device(); if (p_local_device) { @@ -7852,6 +8967,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de memset(&allocatorInfo, 0, sizeof(VmaAllocatorCreateInfo)); allocatorInfo.physicalDevice = p_context->get_physical_device(); allocatorInfo.device = device; + allocatorInfo.instance = p_context->get_instance(); vmaCreateAllocator(&allocatorInfo, &allocator); } @@ -7865,7 +8981,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de VkCommandPoolCreateInfo cmd_pool_info; cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmd_pool_info.pNext = nullptr; - cmd_pool_info.queueFamilyIndex = p_context->get_graphics_queue(); + cmd_pool_info.queueFamilyIndex = p_context->get_graphics_queue_family_index(); cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &frames[i].command_pool); @@ -7931,11 +9047,13 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de } } + // Note: If adding new project settings here, also duplicate their definition in + // rendering_server.cpp for headless doctool. staging_buffer_block_size = GLOBAL_DEF("rendering/vulkan/staging_buffer/block_size_kb", 256); - staging_buffer_block_size = MAX(4, staging_buffer_block_size); + staging_buffer_block_size = MAX(4u, staging_buffer_block_size); staging_buffer_block_size *= 1024; //kb -> bytes staging_buffer_max_size = GLOBAL_DEF("rendering/vulkan/staging_buffer/max_size_mb", 128); - staging_buffer_max_size = MAX(1, staging_buffer_max_size); + staging_buffer_max_size = MAX(1u, staging_buffer_max_size); staging_buffer_max_size *= 1024 * 1024; if (staging_buffer_max_size < staging_buffer_block_size * 4) { @@ -7974,14 +9092,19 @@ void RenderingDeviceVulkan::_free_rids(T &p_owner, const char *p_type) { List<RID> owned; p_owner.get_owned_list(&owned); if (owned.size()) { - WARN_PRINT(itos(owned.size()) + " RIDs of type '" + p_type + "' were leaked."); - for (List<RID>::Element *E = owned.front(); E; E = E->next()) { - free(E->get()); + if (owned.size() == 1) { + WARN_PRINT(vformat("1 RID of type \"%s\" was leaked.", p_type)); + } else { + WARN_PRINT(vformat("%d RIDs of type \"%s\" were leaked.", owned.size(), p_type)); + } + for (const RID &E : owned) { + free(E); } } } void RenderingDeviceVulkan::capture_timestamp(const String &p_name) { + ERR_FAIL_COND_MSG(draw_list != nullptr, "Capturing timestamps during draw list creation is not allowed. Offending timestamp was: " + p_name); ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements); //this should be optional for profiling, else it will slow things down @@ -7991,35 +9114,35 @@ void RenderingDeviceVulkan::capture_timestamp(const String &p_name) { memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; memoryBarrier.pNext = nullptr; memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT | - VK_ACCESS_INDEX_READ_BIT | - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - VK_ACCESS_UNIFORM_READ_BIT | - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | - VK_ACCESS_SHADER_READ_BIT | - VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT | - VK_ACCESS_HOST_READ_BIT | - VK_ACCESS_HOST_WRITE_BIT; + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT; memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT | - VK_ACCESS_INDEX_READ_BIT | - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - VK_ACCESS_UNIFORM_READ_BIT | - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | - VK_ACCESS_SHADER_READ_BIT | - VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT | - VK_ACCESS_HOST_READ_BIT | - VK_ACCESS_HOST_WRITE_BIT; + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT; vkCmdPipelineBarrier(frames[frame].draw_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr); } @@ -8030,6 +9153,92 @@ void RenderingDeviceVulkan::capture_timestamp(const String &p_name) { frames[frame].timestamp_count++; } +uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, RID p_rid, uint64_t p_index) { + _THREAD_SAFE_METHOD_ + + switch (p_resource) { + case DRIVER_RESOURCE_VULKAN_DEVICE: { + return (uint64_t)context->get_device(); + } break; + case DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE: { + return (uint64_t)context->get_physical_device(); + } break; + case DRIVER_RESOURCE_VULKAN_INSTANCE: { + return (uint64_t)context->get_instance(); + } break; + case DRIVER_RESOURCE_VULKAN_QUEUE: { + return (uint64_t)context->get_graphics_queue(); + } break; + case DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX: { + return context->get_graphics_queue_family_index(); + } break; + case DRIVER_RESOURCE_VULKAN_IMAGE: { + Texture *tex = texture_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(tex, 0); + + return (uint64_t)tex->image; + } break; + case DRIVER_RESOURCE_VULKAN_IMAGE_VIEW: { + Texture *tex = texture_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(tex, 0); + + return (uint64_t)tex->view; + } break; + case DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT: { + Texture *tex = texture_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(tex, 0); + + return vulkan_formats[tex->format]; + } break; + case DRIVER_RESOURCE_VULKAN_SAMPLER: { + VkSampler *sampler = sampler_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(sampler, 0); + + return uint64_t(*sampler); + } break; + case DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET: { + UniformSet *uniform_set = uniform_set_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(uniform_set, 0); + + return uint64_t(uniform_set->descriptor_set); + } break; + case DRIVER_RESOURCE_VULKAN_BUFFER: { + Buffer *buffer = nullptr; + if (vertex_buffer_owner.owns(p_rid)) { + buffer = vertex_buffer_owner.get_or_null(p_rid); + } else if (index_buffer_owner.owns(p_rid)) { + buffer = index_buffer_owner.get_or_null(p_rid); + } else if (uniform_buffer_owner.owns(p_rid)) { + buffer = uniform_buffer_owner.get_or_null(p_rid); + } else if (texture_buffer_owner.owns(p_rid)) { + buffer = &texture_buffer_owner.get_or_null(p_rid)->buffer; + } else if (storage_buffer_owner.owns(p_rid)) { + buffer = storage_buffer_owner.get_or_null(p_rid); + } + + ERR_FAIL_NULL_V(buffer, 0); + + return uint64_t(buffer->buffer); + } break; + case DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE: { + ComputePipeline *compute_pipeline = compute_pipeline_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(compute_pipeline, 0); + + return uint64_t(compute_pipeline->pipeline); + } break; + case DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE: { + RenderPipeline *render_pipeline = render_pipeline_owner.get_or_null(p_rid); + ERR_FAIL_NULL_V(render_pipeline, 0); + + return uint64_t(render_pipeline->pipeline); + } break; + default: { + // not supported for this driver + return 0; + } break; + } +} + uint32_t RenderingDeviceVulkan::get_captured_timestamps_count() const { return frames[frame].timestamp_result_count; } @@ -8086,7 +9295,7 @@ String RenderingDeviceVulkan::get_captured_timestamp_name(uint32_t p_index) cons return frames[frame].timestamp_result_names[p_index]; } -int RenderingDeviceVulkan::limit_get(Limit p_limit) { +uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) { switch (p_limit) { case LIMIT_MAX_BOUND_UNIFORM_SETS: return limits.maxBoundDescriptorSets; @@ -8187,19 +9396,23 @@ void RenderingDeviceVulkan::finalize() { List<RID> owned; texture_owner.get_owned_list(&owned); if (owned.size()) { - WARN_PRINT(itos(owned.size()) + " RIDs of type 'Texture' were leaked."); + if (owned.size() == 1) { + WARN_PRINT("1 RID of type \"Texture\" was leaked."); + } else { + WARN_PRINT(vformat("%d RIDs of type \"Texture\" were leaked.", owned.size())); + } //free shared first for (List<RID>::Element *E = owned.front(); E;) { List<RID>::Element *N = E->next(); if (texture_is_shared(E->get())) { free(E->get()); - owned.erase(E->get()); + owned.erase(E); } E = N; } //free non shared second, this will avoid an error trying to free unexisting textures due to dependencies. - for (List<RID>::Element *E = owned.front(); E; E = E->next()) { - free(E->get()); + for (const RID &E : owned) { + free(E); } } } @@ -8226,6 +9439,11 @@ void RenderingDeviceVulkan::finalize() { for (int i = 0; i < staging_buffer_blocks.size(); i++) { vmaDestroyBuffer(allocator, staging_buffer_blocks[i].buffer, staging_buffer_blocks[i].allocation); } + while (small_allocs_pools.size()) { + Map<uint32_t, VmaPool>::Element *E = small_allocs_pools.front(); + vmaDestroyPool(allocator, E->get()); + small_allocs_pools.erase(E); + } vmaDestroyAllocator(allocator); while (vertex_formats.size()) { @@ -8253,6 +9471,7 @@ RenderingDevice *RenderingDeviceVulkan::create_local_device() { } RenderingDeviceVulkan::RenderingDeviceVulkan() { + device_capabilities.device_family = DEVICE_VULKAN; } RenderingDeviceVulkan::~RenderingDeviceVulkan() { diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index a2527d5c33..a4d5af91a4 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,7 +44,11 @@ #endif #include "vk_mem_alloc.h" +#ifdef USE_VOLK +#include <volk.h> +#else #include <vulkan/vulkan.h> +#endif class VulkanContext; @@ -156,6 +160,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t texture_upload_region_size_px = 0; Vector<uint8_t> _texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d = false); + Error _texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier, bool p_use_setup_queue); /*****************/ /**** SAMPLER ****/ @@ -170,7 +175,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // These are temporary buffers on CPU memory that hold // the information until the CPU fetches it and places it // either on GPU buffers, or images (textures). It ensures - // updates are properly synchronized with whathever the + // updates are properly synchronized with whatever the // GPU is doing. // // The logic here is as follows, only 3 of these @@ -234,7 +239,87 @@ class RenderingDeviceVulkan : public RenderingDevice { struct FramebufferFormatKey { Vector<AttachmentFormat> attachments; + Vector<FramebufferPass> passes; + uint32_t view_count = 1; bool operator<(const FramebufferFormatKey &p_key) const { + if (view_count != p_key.view_count) { + return view_count < p_key.view_count; + } + + uint32_t pass_size = passes.size(); + uint32_t key_pass_size = p_key.passes.size(); + if (pass_size != key_pass_size) { + return pass_size < key_pass_size; + } + const FramebufferPass *pass_ptr = passes.ptr(); + const FramebufferPass *key_pass_ptr = p_key.passes.ptr(); + + for (uint32_t i = 0; i < pass_size; i++) { + { //compare color attachments + uint32_t attachment_size = pass_ptr[i].color_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].color_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].color_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].color_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { //compare input attachments + uint32_t attachment_size = pass_ptr[i].input_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].input_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].input_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].input_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { //compare resolve attachments + uint32_t attachment_size = pass_ptr[i].resolve_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].resolve_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].resolve_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].resolve_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + { //compare preserve attachments + uint32_t attachment_size = pass_ptr[i].preserve_attachments.size(); + uint32_t key_attachment_size = key_pass_ptr[i].preserve_attachments.size(); + if (attachment_size != key_attachment_size) { + return attachment_size < key_attachment_size; + } + const int32_t *pass_attachment_ptr = pass_ptr[i].preserve_attachments.ptr(); + const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].preserve_attachments.ptr(); + + for (uint32_t j = 0; j < attachment_size; j++) { + if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) { + return pass_attachment_ptr[j] < key_pass_attachment_ptr[j]; + } + } + } + if (pass_ptr[i].depth_attachment != key_pass_ptr[i].depth_attachment) { + return pass_ptr[i].depth_attachment < key_pass_ptr[i].depth_attachment; + } + } + int as = attachments.size(); int bs = p_key.attachments.size(); if (as != bs) { @@ -261,16 +346,15 @@ class RenderingDeviceVulkan : public RenderingDevice { } }; - VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depthcolor_action, int *r_color_attachment_count = nullptr); - + VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count = 1, Vector<TextureSamples> *r_samples = nullptr); // This is a cache and it's never freed, it ensures // IDs for a given format are always unique. Map<FramebufferFormatKey, FramebufferFormatID> framebuffer_format_cache; struct FramebufferFormat { const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E; VkRenderPass render_pass = VK_NULL_HANDLE; //here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec) - int color_attachments = 0; //used for pipeline validation - TextureSamples samples; + Vector<TextureSamples> pass_samples; + uint32_t view_count = 1; // number of views }; Map<FramebufferFormatID, FramebufferFormat> framebuffer_formats; @@ -282,11 +366,17 @@ class RenderingDeviceVulkan : public RenderingDevice { FinalAction final_color_action; InitialAction initial_depth_action; FinalAction final_depth_action; + uint32_t view_count; + bool operator<(const VersionKey &p_key) const { if (initial_color_action == p_key.initial_color_action) { if (final_color_action == p_key.final_color_action) { if (initial_depth_action == p_key.initial_depth_action) { - return final_depth_action < p_key.final_depth_action; + if (final_depth_action == p_key.final_depth_action) { + return view_count < p_key.view_count; + } else { + return final_depth_action < p_key.final_depth_action; + } } else { return initial_depth_action < p_key.initial_depth_action; } @@ -305,10 +395,12 @@ class RenderingDeviceVulkan : public RenderingDevice { struct Version { VkFramebuffer framebuffer = VK_NULL_HANDLE; VkRenderPass render_pass = VK_NULL_HANDLE; //this one is owned + uint32_t subpass_count = 1; }; Map<VersionKey, Version> framebuffers; Size2 size; + uint32_t view_count; }; RID_Owner<Framebuffer, true> framebuffer_owner; @@ -524,7 +616,7 @@ class RenderingDeviceVulkan : public RenderingDevice { }; uint32_t vertex_input_mask = 0; //inputs used, this is mostly for validation - int fragment_outputs = 0; + uint32_t fragment_output_mask = 0; struct PushConstant { uint32_t push_constant_size = 0; @@ -535,12 +627,19 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t compute_local_size[3] = { 0, 0, 0 }; + struct SpecializationConstant { + PipelineSpecializationConstant constant; + uint32_t stage_flags = 0; + }; + bool is_compute = false; int max_output = 0; Vector<Set> sets; Vector<uint32_t> set_formats; Vector<VkPipelineShaderStageCreateInfo> pipeline_stages; + Vector<SpecializationConstant> specialization_constants; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; + String name; //used for debug }; String _shader_uniform_debug(RID p_shader, int p_set = -1); @@ -558,8 +657,8 @@ class RenderingDeviceVulkan : public RenderingDevice { // Basically, you can mix and match pools as you // like, but you'll run into fragmentation issues. // Because of this, the recommended approach is to - // create a a pool for every descriptor set type, - // as this prevents fragmentation. + // create a pool for every descriptor set type, as + // this prevents fragmentation. // // This is implemented here as a having a list of // pools (each can contain up to 64 sets) for each @@ -644,6 +743,8 @@ class RenderingDeviceVulkan : public RenderingDevice { LocalVector<AttachableTexture> attachable_textures; //used for validation Vector<Texture *> mutable_sampled_textures; //used for layout change Vector<Texture *> mutable_storage_textures; //used for layout change + UniformSetInvalidatedCallback invalidated_callback = nullptr; + void *invalidated_callback_userdata = nullptr; }; RID_Owner<UniformSet, true> uniform_set_owner; @@ -668,6 +769,7 @@ class RenderingDeviceVulkan : public RenderingDevice { #ifdef DEBUG_ENABLED struct Validation { FramebufferFormatID framebuffer_format = 0; + uint32_t render_pass = 0; uint32_t dynamic_state = 0; VertexFormatID vertex_format = 0; bool uses_restart_indices = false; @@ -711,7 +813,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // When using split command lists, this is // implemented internally using secondary command // buffers. As they can be created in threads, - // each needs it's own command pool. + // each needs its own command pool. struct SplitDrawListAllocator { VkCommandPool command_pool = VK_NULL_HANDLE; @@ -723,6 +825,7 @@ class RenderingDeviceVulkan : public RenderingDevice { struct DrawList { VkCommandBuffer command_buffer = VK_NULL_HANDLE; // If persistent, this is owned, otherwise it's shared with the ringbuffer. Rect2i viewport; + bool viewport_set = false; struct SetState { uint32_t pipeline_expected_format = 0; @@ -746,7 +849,6 @@ class RenderingDeviceVulkan : public RenderingDevice { #ifdef DEBUG_ENABLED struct Validation { bool active = true; // Means command buffer was not closed, so you can keep adding things. - FramebufferFormatID framebuffer_format = INVALID_ID; // Actual render pass values. uint32_t dynamic_state = 0; VertexFormatID vertex_format = INVALID_ID; @@ -782,7 +884,15 @@ class RenderingDeviceVulkan : public RenderingDevice { }; DrawList *draw_list = nullptr; // One for regular draw lists, multiple for split. + uint32_t draw_list_subpass_count = 0; uint32_t draw_list_count = 0; + VkRenderPass draw_list_render_pass; + VkFramebuffer draw_list_vkframebuffer; +#ifdef DEBUG_ENABLED + FramebufferFormatID draw_list_framebuffer_format = INVALID_ID; +#endif + uint32_t draw_list_current_subpass = 0; + bool draw_list_split = false; Vector<RID> draw_list_bound_textures; Vector<RID> draw_list_storage_textures; @@ -790,10 +900,12 @@ class RenderingDeviceVulkan : public RenderingDevice { bool draw_list_unbind_depth_textures = false; void _draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); - Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass); + Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count); Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); Buffer *_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &dst_stage_mask, VkAccessFlags &dst_access, uint32_t p_post_barrier); + Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass); + void _draw_list_free(Rect2i *r_last_viewport = nullptr); /**********************/ /**** COMPUTE LIST ****/ @@ -904,9 +1016,14 @@ class RenderingDeviceVulkan : public RenderingDevice { void _free_pending_resources(int p_frame); VmaAllocator allocator = nullptr; + Map<uint32_t, VmaPool> small_allocs_pools; + VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index); VulkanContext *context = nullptr; + uint64_t image_memory = 0; + uint64_t buffer_memory = 0; + void _free_internal(RID p_id); void _flush(bool p_current_frame); @@ -921,14 +1038,16 @@ class RenderingDeviceVulkan : public RenderingDevice { public: virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()); virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture); + virtual RID texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, uint64_t p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers); - virtual RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, TextureSliceType p_slice_type = TEXTURE_SLICE_2D); + virtual RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D); virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); virtual Vector<uint8_t> texture_get_data(RID p_texture, uint32_t p_layer); virtual bool texture_is_format_supported_for_usage(DataFormat p_format, uint32_t p_usage) const; virtual bool texture_is_shared(RID p_texture); virtual bool texture_is_valid(RID p_texture); + virtual Size2i texture_size(RID p_texture); virtual Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, uint32_t p_post_barrier = BARRIER_MASK_ALL); virtual Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, uint32_t p_post_barrier = BARRIER_MASK_ALL); @@ -938,11 +1057,13 @@ public: /**** FRAMEBUFFER ****/ /*********************/ - virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format); + virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1); + virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1); virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1); - virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format); + virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0); - virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID); + virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); + virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID); virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer); @@ -971,7 +1092,11 @@ public: /**** SHADER ****/ /****************/ - virtual RID shader_create(const Vector<ShaderStageData> &p_stages); + virtual String shader_get_binary_cache_key() const; + virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = ""); + + virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary); + virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader); /*****************/ @@ -984,6 +1109,7 @@ public: virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set); virtual bool uniform_set_is_valid(RID p_uniform_set); + virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata); virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); //works for any buffer virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, uint32_t p_post_barrier = BARRIER_MASK_ALL); @@ -993,14 +1119,14 @@ public: /**** RENDER PIPELINE ****/ /*************************/ - virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0); + virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()); virtual bool render_pipeline_is_valid(RID p_pipeline); /**************************/ /**** COMPUTE PIPELINE ****/ /**************************/ - virtual RID compute_pipeline_create(RID p_shader); + virtual RID compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>()); virtual bool compute_pipeline_is_valid(RID p_pipeline); /****************/ @@ -1032,6 +1158,10 @@ public: virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect); virtual void draw_list_disable_scissor(DrawListID p_list); + virtual uint32_t draw_list_get_current_pass(); + virtual DrawListID draw_list_switch_to_next_pass(); + virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids); + virtual void draw_list_end(uint32_t p_post_barrier = BARRIER_MASK_ALL); /***********************/ @@ -1073,7 +1203,7 @@ public: /**** Limits ****/ /****************/ - virtual int limit_get(Limit p_limit); + virtual uint64_t limit_get(Limit p_limit); virtual void prepare_screen_for_drawing(); void initialize(VulkanContext *p_context, bool p_local_device = false); @@ -1088,7 +1218,7 @@ public: virtual RenderingDevice *create_local_device(); - virtual uint64_t get_memory_usage() const; + virtual uint64_t get_memory_usage(MemoryType p_type) const; virtual void set_resource_name(RID p_id, const String p_name); @@ -1098,8 +1228,11 @@ public: virtual String get_device_vendor_name() const; virtual String get_device_name() const; + virtual RenderingDevice::DeviceType get_device_type() const; virtual String get_device_pipeline_cache_uuid() const; + virtual uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0); + RenderingDeviceVulkan(); ~RenderingDeviceVulkan(); }; diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index c564cee757..3551b5d6c4 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,9 @@ #include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/string/ustring.h" +#include "core/templates/local_vector.h" #include "core/version.h" +#include "servers/rendering/rendering_device.h" #include "vk_enum_string_helper.h" @@ -44,6 +46,8 @@ #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define APP_SHORT_NAME "GodotEngine" +VulkanHooks *VulkanContext::vulkan_hooks = nullptr; + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -135,10 +139,10 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( } String error_message(type_string + - " - Message Id Number: " + String::num_int64(pCallbackData->messageIdNumber) + - " | Message Id Name: " + pCallbackData->pMessageIdName + - "\n\t" + pCallbackData->pMessage + - objects_string + labels_string); + " - Message Id Number: " + String::num_int64(pCallbackData->messageIdNumber) + + " | Message Id Name: " + pCallbackData->pMessageIdName + + "\n\t" + pCallbackData->pMessage + + objects_string + labels_string); // Convert VK severity to our own log macros. switch (messageSeverity) { @@ -163,7 +167,36 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( return VK_FALSE; } -VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers) { +VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_report_callback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char *pLayerPrefix, + const char *pMessage, + void *pUserData) { + String debugMessage = String("Vulkan Debug Report: object - ") + + String::num_int64(object) + "\n" + pMessage; + + switch (flags) { + case VK_DEBUG_REPORT_DEBUG_BIT_EXT: + case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: + print_line(debugMessage); + break; + case VK_DEBUG_REPORT_WARNING_BIT_EXT: + case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: + WARN_PRINT(debugMessage); + break; + case VK_DEBUG_REPORT_ERROR_BIT_EXT: + ERR_PRINT(debugMessage); + break; + } + + return VK_FALSE; +} + +VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers) { for (uint32_t i = 0; i < check_count; i++) { VkBool32 found = 0; for (uint32_t j = 0; j < layer_count; j++) { @@ -180,55 +213,85 @@ VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char **check_n return 1; } -Error VulkanContext::_create_validation_layers() { +Error VulkanContext::_get_preferred_validation_layers(uint32_t *count, const char *const **names) { + static const LocalVector<LocalVector<const char *>> instance_validation_layers_alt{ + // Preferred set of validation layers + { "VK_LAYER_KHRONOS_validation" }, + + // Alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers + { "VK_LAYER_LUNARG_standard_validation" }, + + // Alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers + { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" } + }; + + // Clear out-arguments + *count = 0; + if (names != nullptr) { + *names = nullptr; + } + VkResult err; - const char *instance_validation_layers_alt1[] = { "VK_LAYER_KHRONOS_validation" }; - const char *instance_validation_layers_alt2[] = { "VK_LAYER_LUNARG_standard_validation" }; - const char *instance_validation_layers_alt3[] = { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" }; + uint32_t instance_layer_count; - uint32_t instance_layer_count = 0; err = vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + if (err) { + ERR_FAIL_V(ERR_CANT_CREATE); + } - VkBool32 validation_found = 0; - uint32_t validation_layer_count = 0; - const char **instance_validation_layers = nullptr; - if (instance_layer_count > 0) { - VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count); - err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers); - if (err) { - free(instance_layers); - ERR_FAIL_V(ERR_CANT_CREATE); - } + if (instance_layer_count < 1) { + return OK; + } - validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt1); - instance_validation_layers = instance_validation_layers_alt1; - validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers); + VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers); + if (err) { + free(instance_layers); + ERR_FAIL_V(ERR_CANT_CREATE); + } - // use alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers - if (!validation_found) { - validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt2); - instance_validation_layers = instance_validation_layers_alt2; - validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers); + for (uint32_t i = 0; i < instance_validation_layers_alt.size(); i++) { + if (_check_layers(instance_validation_layers_alt[i].size(), instance_validation_layers_alt[i].ptr(), instance_layer_count, instance_layers)) { + *count = instance_validation_layers_alt[i].size(); + if (names != nullptr) { + *names = instance_validation_layers_alt[i].ptr(); + } + break; } + } - // use alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers - if (!validation_found) { - validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt3); - instance_validation_layers = instance_validation_layers_alt3; - validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers); - } + free(instance_layers); - free(instance_layers); - } + return OK; +} - if (validation_found) { - enabled_layer_count = validation_layer_count; - for (uint32_t i = 0; i < validation_layer_count; i++) { - enabled_layers[i] = instance_validation_layers[i]; +typedef VkResult(VKAPI_PTR *_vkEnumerateInstanceVersion)(uint32_t *); + +Error VulkanContext::_obtain_vulkan_version() { + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html#_description + // for Vulkan 1.0 vkEnumerateInstanceVersion is not available, including not in the loader we compile against on Android. + _vkEnumerateInstanceVersion func = (_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"); + if (func != nullptr) { + uint32_t api_version; + VkResult res = func(&api_version); + if (res == VK_SUCCESS) { + vulkan_major = VK_API_VERSION_MAJOR(api_version); + vulkan_minor = VK_API_VERSION_MINOR(api_version); + vulkan_patch = VK_API_VERSION_PATCH(api_version); + } else { + // according to the documentation this shouldn't fail with anything except a memory allocation error + // in which case we're in deep trouble anyway + ERR_FAIL_V(ERR_CANT_CREATE); } } else { - return ERR_CANT_CREATE; + print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0."); + } + + // we don't go above 1.2 + if ((vulkan_major > 1) || (vulkan_major == 1 && vulkan_minor > 2)) { + vulkan_major = 1; + vulkan_minor = 2; + vulkan_patch = 0; } return OK; @@ -238,8 +301,8 @@ Error VulkanContext::_initialize_extensions() { uint32_t instance_extension_count = 0; enabled_extension_count = 0; - enabled_layer_count = 0; enabled_debug_utils = false; + enabled_debug_report = false; /* Look for instance extensions */ VkBool32 surfaceExtFound = 0; VkBool32 platformSurfaceExtFound = 0; @@ -266,14 +329,18 @@ Error VulkanContext::_initialize_extensions() { extension_names[enabled_extension_count++] = _get_platform_surface_extension(); } if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, instance_extensions[i].extensionName)) { - if (use_validation_layers) { + if (_use_validation_layers()) { extension_names[enabled_extension_count++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME; + enabled_debug_report = true; } } if (!strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instance_extensions[i].extensionName)) { extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; enabled_debug_utils = true; } + if (!strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, instance_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + } if (enabled_extension_count >= MAX_EXTENSIONS) { free(instance_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); @@ -289,12 +356,289 @@ Error VulkanContext::_initialize_extensions() { return OK; } -Error VulkanContext::_create_physical_device() { - /* Look for validation layers */ - if (use_validation_layers) { - _create_validation_layers(); +uint32_t VulkanContext::SubgroupCapabilities::supported_stages_flags_rd() const { + uint32_t flags = 0; + + if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_VERTEX_BIT; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_EVALUATION_BIT; + } + // if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { + // flags += RenderingDevice::ShaderStage::SHADER_STAGE_GEOMETRY_BIT; + // } + if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_FRAGMENT_BIT; + } + if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { + flags += RenderingDevice::ShaderStage::SHADER_STAGE_COMPUTE_BIT; + } + + return flags; +} + +String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { + String res; + + if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { + res += ", STAGE_VERTEX"; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { + res += ", STAGE_TESSELLATION_CONTROL"; + } + if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { + res += ", STAGE_TESSELLATION_EVALUATION"; + } + if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { + res += ", STAGE_GEOMETRY"; + } + if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { + res += ", STAGE_FRAGMENT"; + } + if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { + res += ", STAGE_COMPUTE"; + } + + /* these are not defined on Android GRMBL */ + if (supportedStages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) { + res += ", STAGE_RAYGEN_KHR"; + } + if (supportedStages & 0x00000200 /* VK_SHADER_STAGE_ANY_HIT_BIT_KHR */) { + res += ", STAGE_ANY_HIT_KHR"; + } + if (supportedStages & 0x00000400 /* VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR */) { + res += ", STAGE_CLOSEST_HIT_KHR"; + } + if (supportedStages & 0x00000800 /* VK_SHADER_STAGE_MISS_BIT_KHR */) { + res += ", STAGE_MISS_KHR"; + } + if (supportedStages & 0x00001000 /* VK_SHADER_STAGE_INTERSECTION_BIT_KHR */) { + res += ", STAGE_INTERSECTION_KHR"; + } + if (supportedStages & 0x00002000 /* VK_SHADER_STAGE_CALLABLE_BIT_KHR */) { + res += ", STAGE_CALLABLE_KHR"; + } + if (supportedStages & 0x00000040 /* VK_SHADER_STAGE_TASK_BIT_NV */) { + res += ", STAGE_TASK_NV"; + } + if (supportedStages & 0x00000080 /* VK_SHADER_STAGE_MESH_BIT_NV */) { + res += ", STAGE_MESH_NV"; + } + + return res.substr(2); // remove first ", " +} + +uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const { + uint32_t flags = 0; + + if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_BASIC_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_VOTE_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_BALLOT_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_CLUSTERED_BIT; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { + flags += RenderingDevice::SubgroupOperations::SUBGROUP_QUAD_BIT; + } + + return flags; +} + +String VulkanContext::SubgroupCapabilities::supported_operations_desc() const { + String res; + + if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { + res += ", FEATURE_BASIC"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { + res += ", FEATURE_VOTE"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { + res += ", FEATURE_ARITHMETIC"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { + res += ", FEATURE_BALLOT"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { + res += ", FEATURE_SHUFFLE"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { + res += ", FEATURE_SHUFFLE_RELATIVE"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { + res += ", FEATURE_CLUSTERED"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { + res += ", FEATURE_QUAD"; + } + if (supportedOperations & VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV) { + res += ", FEATURE_PARTITIONED_NV"; + } + + return res.substr(2); // remove first ", " +} + +Error VulkanContext::_check_capabilities() { + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_multiview.html + // https://www.khronos.org/blog/vulkan-subgroup-tutorial + + // for Vulkan 1.0 vkGetPhysicalDeviceProperties2 is not available, including not in the loader we compile against on Android. + + // so we check if the functions are accessible by getting their function pointers and skipping if not + // (note that the desktop loader does a better job here but the android loader doesn't) + + // assume not supported until proven otherwise + multiview_capabilities.is_supported = false; + multiview_capabilities.geometry_shader_is_supported = false; + multiview_capabilities.tessellation_shader_is_supported = false; + multiview_capabilities.max_view_count = 0; + multiview_capabilities.max_instance_count = 0; + subgroup_capabilities.size = 0; + subgroup_capabilities.supportedStages = 0; + subgroup_capabilities.supportedOperations = 0; + subgroup_capabilities.quadOperationsInAllStages = false; + shader_capabilities.shader_float16_is_supported = false; + shader_capabilities.shader_int8_is_supported = false; + storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = false; + storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = false; + storage_buffer_capabilities.storage_push_constant_16_is_supported = false; + storage_buffer_capabilities.storage_input_output_16 = false; + + // check for extended features + PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2"); + if (vkGetPhysicalDeviceFeatures2_func == nullptr) { + // In Vulkan 1.0 might be accessible under its original extension name + vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); + } + if (vkGetPhysicalDeviceFeatures2_func != nullptr) { + // check our extended features + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, + /*pNext*/ nullptr, + /*shaderFloat16*/ false, + /*shaderInt8*/ false, + }; + + VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, + /*pNext*/ &shader_features, + /*storageBuffer16BitAccess*/ false, + /*uniformAndStorageBuffer16BitAccess*/ false, + /*storagePushConstant16*/ false, + /*storageInputOutput16*/ false, + }; + + VkPhysicalDeviceMultiviewFeatures multiview_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, + /*pNext*/ &storage_feature, + /*multiview*/ false, + /*multiviewGeometryShader*/ false, + /*multiviewTessellationShader*/ false, + }; + + VkPhysicalDeviceFeatures2 device_features; + device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + device_features.pNext = &multiview_features; + + vkGetPhysicalDeviceFeatures2_func(gpu, &device_features); + + multiview_capabilities.is_supported = multiview_features.multiview; + multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; + multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; + + shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; + shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; + + storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess; + storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess; + storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16; + storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16; } + // check extended properties + PFN_vkGetPhysicalDeviceProperties2 device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2"); + if (device_properties_func == nullptr) { + // In Vulkan 1.0 might be accessible under its original extension name + device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR"); + } + if (device_properties_func != nullptr) { + VkPhysicalDeviceMultiviewProperties multiviewProperties; + VkPhysicalDeviceSubgroupProperties subgroupProperties; + VkPhysicalDeviceProperties2 physicalDeviceProperties; + + subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + subgroupProperties.pNext = nullptr; + + physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + + if (multiview_capabilities.is_supported) { + multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; + multiviewProperties.pNext = &subgroupProperties; + + physicalDeviceProperties.pNext = &multiviewProperties; + } else { + physicalDeviceProperties.pNext = &subgroupProperties; + } + + device_properties_func(gpu, &physicalDeviceProperties); + + subgroup_capabilities.size = subgroupProperties.subgroupSize; + subgroup_capabilities.supportedStages = subgroupProperties.supportedStages; + subgroup_capabilities.supportedOperations = subgroupProperties.supportedOperations; + // Note: quadOperationsInAllStages will be true if: + // - supportedStages has VK_SHADER_STAGE_ALL_GRAPHICS + VK_SHADER_STAGE_COMPUTE_BIT + // - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT + subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages; + + if (multiview_capabilities.is_supported) { + multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount; + multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex; + + print_verbose("- Vulkan multiview supported:"); + print_verbose(" max view count: " + itos(multiview_capabilities.max_view_count)); + print_verbose(" max instances: " + itos(multiview_capabilities.max_instance_count)); + } else { + print_verbose("- Vulkan multiview not supported"); + } + + print_verbose("- Vulkan subgroup:"); + print_verbose(" size: " + itos(subgroup_capabilities.size)); + print_verbose(" stages: " + subgroup_capabilities.supported_stages_desc()); + print_verbose(" supported ops: " + subgroup_capabilities.supported_operations_desc()); + if (subgroup_capabilities.quadOperationsInAllStages) { + print_verbose(" quad operations in all stages"); + } + } else { + print_verbose("- Couldn't call vkGetPhysicalDeviceProperties2"); + } + + return OK; +} + +Error VulkanContext::_create_instance() { + /* obtain version */ + _obtain_vulkan_version(); + + /* initialise extensions */ { Error err = _initialize_extensions(); if (err != OK) { @@ -303,34 +647,31 @@ Error VulkanContext::_create_physical_device() { } CharString cs = ProjectSettings::get_singleton()->get("application/config/name").operator String().utf8(); - String name = "GodotEngine " + String(VERSION_FULL_NAME); - CharString namecs = name.utf8(); const VkApplicationInfo app = { /*sType*/ VK_STRUCTURE_TYPE_APPLICATION_INFO, /*pNext*/ nullptr, /*pApplicationName*/ cs.get_data(), /*applicationVersion*/ 0, - /*pEngineName*/ namecs.get_data(), - /*engineVersion*/ 0, - /*apiVersion*/ VK_API_VERSION_1_0, - }; - VkInstanceCreateInfo inst_info = { - /*sType*/ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ 0, - /*pApplicationInfo*/ &app, - /*enabledLayerCount*/ enabled_layer_count, - /*ppEnabledLayerNames*/ (const char *const *)enabled_layers, - /*enabledExtensionCount*/ enabled_extension_count, - /*ppEnabledExtensionNames*/ (const char *const *)extension_names, + /*pEngineName*/ VERSION_NAME, + /*engineVersion*/ VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH), + /*apiVersion*/ VK_MAKE_VERSION(vulkan_major, vulkan_minor, 0) }; + VkInstanceCreateInfo inst_info{}; + inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + inst_info.pApplicationInfo = &app; + inst_info.enabledExtensionCount = enabled_extension_count; + inst_info.ppEnabledExtensionNames = (const char *const *)extension_names; + if (_use_validation_layers()) { + _get_preferred_validation_layers(&inst_info.enabledLayerCount, &inst_info.ppEnabledLayerNames); + } /* - * This is info for a temp callback to use during CreateInstance. - * After the instance is created, we use the instance-based - * function to register the final callback. - */ + * This is info for a temp callback to use during CreateInstance. + * After the instance is created, we use the instance-based + * function to register the final callback. + */ VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info; + VkDebugReportCallbackCreateInfoEXT dbg_report_callback_create_info{}; if (enabled_debug_utils) { // VK_EXT_debug_utils style dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; @@ -339,35 +680,130 @@ Error VulkanContext::_create_physical_device() { dbg_messenger_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; dbg_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; dbg_messenger_create_info.pfnUserCallback = _debug_messenger_callback; dbg_messenger_create_info.pUserData = this; inst_info.pNext = &dbg_messenger_create_info; + } else if (enabled_debug_report) { + dbg_report_callback_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + dbg_report_callback_create_info.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT | + VK_DEBUG_REPORT_WARNING_BIT_EXT | + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | + VK_DEBUG_REPORT_ERROR_BIT_EXT | + VK_DEBUG_REPORT_DEBUG_BIT_EXT; + dbg_report_callback_create_info.pfnCallback = _debug_report_callback; + dbg_report_callback_create_info.pUserData = this; + inst_info.pNext = &dbg_report_callback_create_info; } - uint32_t gpu_count; + VkResult err; - VkResult err = vkCreateInstance(&inst_info, nullptr, &inst); - ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE, - "Cannot find a compatible Vulkan installable client driver (ICD).\n\n" - "vkCreateInstance Failure"); - ERR_FAIL_COND_V_MSG(err == VK_ERROR_EXTENSION_NOT_PRESENT, ERR_CANT_CREATE, - "Cannot find a specified extension library.\n" - "Make sure your layers path is set appropriately.\n" - "vkCreateInstance Failure"); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, - "vkCreateInstance failed.\n\n" - "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" - "Please look at the Getting Started guide for additional information.\n" - "vkCreateInstance Failure"); + if (vulkan_hooks) { + if (!vulkan_hooks->create_vulkan_instance(&inst_info, &inst)) { + return ERR_CANT_CREATE; + } + } else { + err = vkCreateInstance(&inst_info, nullptr, &inst); + ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE, + "Cannot find a compatible Vulkan installable client driver (ICD).\n\n" + "vkCreateInstance Failure"); + ERR_FAIL_COND_V_MSG(err == VK_ERROR_EXTENSION_NOT_PRESENT, ERR_CANT_CREATE, + "Cannot find a specified extension library.\n" + "Make sure your layers path is set appropriately.\n" + "vkCreateInstance Failure"); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, + "vkCreateInstance failed.\n\n" + "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" + "Please look at the Getting Started guide for additional information.\n" + "vkCreateInstance Failure"); + } inst_initialized = true; +#ifdef USE_VOLK + volkLoadInstance(inst); +#endif + + if (enabled_debug_utils) { + // Setup VK_EXT_debug_utils function pointers always (we use them for + // debug labels and names). + CreateDebugUtilsMessengerEXT = + (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT"); + DestroyDebugUtilsMessengerEXT = + (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT"); + SubmitDebugUtilsMessageEXT = + (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT"); + CmdBeginDebugUtilsLabelEXT = + (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT"); + CmdEndDebugUtilsLabelEXT = + (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT"); + CmdInsertDebugUtilsLabelEXT = + (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT"); + SetDebugUtilsObjectNameEXT = + (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT"); + if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT || + nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT || + nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT || + nullptr == SetDebugUtilsObjectNameEXT) { + ERR_FAIL_V_MSG(ERR_CANT_CREATE, + "GetProcAddr: Failed to init VK_EXT_debug_utils\n" + "GetProcAddr: Failure"); + } + + err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger); + switch (err) { + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, + "CreateDebugUtilsMessengerEXT: out of host memory\n" + "CreateDebugUtilsMessengerEXT Failure"); + break; + default: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, + "CreateDebugUtilsMessengerEXT: unknown failure\n" + "CreateDebugUtilsMessengerEXT Failure"); + ERR_FAIL_V(ERR_CANT_CREATE); + break; + } + } else if (enabled_debug_report) { + CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT"); + DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT"); + DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT"); + + if (nullptr == CreateDebugReportCallbackEXT || nullptr == DebugReportMessageEXT || nullptr == DestroyDebugReportCallbackEXT) { + ERR_FAIL_V_MSG(ERR_CANT_CREATE, + "GetProcAddr: Failed to init VK_EXT_debug_report\n" + "GetProcAddr: Failure"); + } + + err = CreateDebugReportCallbackEXT(inst, &dbg_report_callback_create_info, nullptr, &dbg_debug_report); + switch (err) { + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, + "CreateDebugReportCallbackEXT: out of host memory\n" + "CreateDebugReportCallbackEXT Failure"); + break; + default: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, + "CreateDebugReportCallbackEXT: unknown failure\n" + "CreateDebugReportCallbackEXT Failure"); + ERR_FAIL_V(ERR_CANT_CREATE); + break; + } + } + + return OK; +} + +Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { /* Make initial call to query gpu_count, then second call for gpu info*/ - err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr); + uint32_t gpu_count = 0; + VkResult err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - ERR_FAIL_COND_V_MSG(gpu_count == 0, ERR_CANT_CREATE, "vkEnumeratePhysicalDevices reported zero accessible devices.\n\n" "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" @@ -379,19 +815,6 @@ Error VulkanContext::_create_physical_device() { free(physical_devices); ERR_FAIL_V(ERR_CANT_CREATE); } - /* for now, just grab the first physical device */ - uint32_t device_index = 0; - gpu = physical_devices[device_index]; - free(physical_devices); - - /* Look for device extensions */ - uint32_t device_extension_count = 0; - VkBool32 swapchainExtFound = 0; - enabled_extension_count = 0; - memset(extension_names, 0, sizeof(extension_names)); - - /* Get identifier properties */ - vkGetPhysicalDeviceProperties(gpu, &gpu_props); static const struct { uint32_t id; @@ -399,13 +822,143 @@ Error VulkanContext::_create_physical_device() { } vendor_names[] = { { 0x1002, "AMD" }, { 0x1010, "ImgTec" }, + { 0x106B, "Apple" }, { 0x10DE, "NVIDIA" }, { 0x13B5, "ARM" }, { 0x5143, "Qualcomm" }, - { 0x8086, "INTEL" }, + { 0x8086, "Intel" }, { 0, nullptr }, }; + + int32_t device_index = -1; + if (vulkan_hooks) { + if (!vulkan_hooks->get_physical_device(&gpu)) { + return ERR_CANT_CREATE; + } + + // not really needed but nice to print the correct entry + for (uint32_t i = 0; i < gpu_count; ++i) { + if (physical_devices[i] == gpu) { + device_index = i; + break; + } + } + } else { + // TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances. + // The device should really be a preference, but for now choosing a discrete GPU over the + // integrated one is better than the default. + + int type_selected = -1; + print_verbose("Vulkan devices:"); + for (uint32_t i = 0; i < gpu_count; ++i) { + VkPhysicalDeviceProperties props; + vkGetPhysicalDeviceProperties(physical_devices[i], &props); + + bool present_supported = false; + + uint32_t device_queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr); + VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props); + for (uint32_t j = 0; j < device_queue_family_count; j++) { + VkBool32 supports; + vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports); + if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) { + present_supported = true; + } else { + continue; + } + } + String name = props.deviceName; + String vendor = "Unknown"; + String dev_type; + switch (props.deviceType) { + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { + dev_type = "Discrete"; + } break; + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { + dev_type = "Integrated"; + } break; + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { + dev_type = "Virtual"; + } break; + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { + dev_type = "CPU"; + } break; + default: { + dev_type = "Other"; + } break; + } + uint32_t vendor_idx = 0; + while (vendor_names[vendor_idx].name != nullptr) { + if (props.vendorID == vendor_names[vendor_idx].id) { + vendor = vendor_names[vendor_idx].name; + break; + } + vendor_idx++; + } + free(device_queue_props); + print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + dev_type); + + if (present_supported) { // Select first supported device of preffered type: Discrete > Integrated > Virtual > CPU > Other. + switch (props.deviceType) { + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { + if (type_selected < 4) { + type_selected = 4; + device_index = i; + } + } break; + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { + if (type_selected < 3) { + type_selected = 3; + device_index = i; + } + } break; + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { + if (type_selected < 2) { + type_selected = 2; + device_index = i; + } + } break; + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { + if (type_selected < 1) { + type_selected = 1; + device_index = i; + } + } break; + default: { + if (type_selected < 0) { + type_selected = 0; + device_index = i; + } + } break; + } + } + } + + int32_t user_device_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU. + if (user_device_index >= 0 && user_device_index < (int32_t)gpu_count) { + device_index = user_device_index; + } + + ERR_FAIL_COND_V_MSG(device_index == -1, ERR_CANT_CREATE, "None of Vulkan devices supports both graphics and present queues."); + + gpu = physical_devices[device_index]; + } + + free(physical_devices); + + /* Look for device extensions */ + uint32_t device_extension_count = 0; + VkBool32 swapchainExtFound = 0; + enabled_extension_count = 0; + memset(extension_names, 0, sizeof(extension_names)); + + /* Get identifier properties */ + vkGetPhysicalDeviceProperties(gpu, &gpu_props); + device_name = gpu_props.deviceName; + device_type = gpu_props.deviceType; pipeline_cache_id = String::hex_encode_buffer(gpu_props.pipelineCacheUUID, VK_UUID_SIZE); pipeline_cache_id += "-driver-" + itos(gpu_props.driverVersion); { @@ -419,9 +972,11 @@ Error VulkanContext::_create_physical_device() { vendor_idx++; } } -#ifdef DEBUG_ENABLED - print_line("Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name); -#endif + + print_line( + "Vulkan API " + itos(vulkan_major) + "." + itos(vulkan_minor) + "." + itos(vulkan_patch) + + " - " + "Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name); + device_api_version = gpu_props.apiVersion; err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, nullptr); @@ -440,6 +995,10 @@ Error VulkanContext::_create_physical_device() { swapchainExtFound = 1; extension_names[enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME; } + if (!strcmp(VK_KHR_MULTIVIEW_EXTENSION_NAME, device_extensions[i].extensionName)) { + // if multiview is supported, enable it + extension_names[enabled_extension_count++] = VK_KHR_MULTIVIEW_EXTENSION_NAME; + } if (enabled_extension_count >= MAX_EXTENSIONS) { free(device_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); @@ -490,51 +1049,7 @@ Error VulkanContext::_create_physical_device() { " extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\n" "vkCreateInstance Failure"); - if (enabled_debug_utils) { - // Setup VK_EXT_debug_utils function pointers always (we use them for - // debug labels and names). - CreateDebugUtilsMessengerEXT = - (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT"); - DestroyDebugUtilsMessengerEXT = - (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT"); - SubmitDebugUtilsMessageEXT = - (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT"); - CmdBeginDebugUtilsLabelEXT = - (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT"); - CmdEndDebugUtilsLabelEXT = - (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT"); - CmdInsertDebugUtilsLabelEXT = - (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT"); - SetDebugUtilsObjectNameEXT = - (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT"); - if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT || - nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT || - nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT || - nullptr == SetDebugUtilsObjectNameEXT) { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, - "GetProcAddr: Failed to init VK_EXT_debug_utils\n" - "GetProcAddr: Failure"); - } - - err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger); - switch (err) { - case VK_SUCCESS: - break; - case VK_ERROR_OUT_OF_HOST_MEMORY: - ERR_FAIL_V_MSG(ERR_CANT_CREATE, - "CreateDebugUtilsMessengerEXT: out of host memory\n" - "CreateDebugUtilsMessengerEXT Failure"); - break; - default: - ERR_FAIL_V_MSG(ERR_CANT_CREATE, - "CreateDebugUtilsMessengerEXT: unknown failure\n" - "CreateDebugUtilsMessengerEXT Failure"); - ERR_FAIL_V(ERR_CANT_CREATE); - break; - } - } - - /* Call with NULL data to get count */ + /* Call with nullptr data to get count */ vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, nullptr); ERR_FAIL_COND_V(queue_family_count == 0, ERR_CANT_CREATE); @@ -561,6 +1076,15 @@ Error VulkanContext::_create_physical_device() { GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfacePresentModesKHR); GET_INSTANCE_PROC_ADDR(inst, GetSwapchainImagesKHR); + // get info about what our vulkan driver is capable off + { + Error res = _check_capabilities(); + if (res != OK) { + return res; + } + } + + device_initialized = true; return OK; } @@ -575,9 +1099,61 @@ Error VulkanContext::_create_device() { queues[0].pQueuePriorities = queue_priorities; queues[0].flags = 0; + // Before we retrieved what is supported, here we tell Vulkan we want to enable these features using the same structs. + void *nextptr = nullptr; + + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, + /*pNext*/ nextptr, + /*shaderFloat16*/ shader_capabilities.shader_float16_is_supported, + /*shaderInt8*/ shader_capabilities.shader_int8_is_supported, + }; + nextptr = &shader_features; + + VkPhysicalDeviceVulkan11Features vulkan11features; + VkPhysicalDevice16BitStorageFeaturesKHR storage_feature; + VkPhysicalDeviceMultiviewFeatures multiview_features; + if (vulkan_major > 1 || vulkan_minor >= 2) { + // In Vulkan 1.2 and newer we use a newer struct to enable various features + + vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + vulkan11features.pNext = nextptr; + vulkan11features.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; + vulkan11features.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; + vulkan11features.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; + vulkan11features.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; + vulkan11features.multiview = multiview_capabilities.is_supported; + vulkan11features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + vulkan11features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + vulkan11features.variablePointersStorageBuffer = 0; + vulkan11features.variablePointers = 0; + vulkan11features.protectedMemory = 0; + vulkan11features.samplerYcbcrConversion = 0; + vulkan11features.shaderDrawParameters = 0; + nextptr = &vulkan11features; + } else { + // On Vulkan 1.0 and 1.1 we use our older structs to initialise these features + storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; + storage_feature.pNext = nextptr; + storage_feature.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; + storage_feature.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; + storage_feature.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; + storage_feature.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; + nextptr = &storage_feature; + + if (vulkan_major == 1 && vulkan_minor == 1) { + multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + multiview_features.pNext = nextptr; + multiview_features.multiview = multiview_capabilities.is_supported; + multiview_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + multiview_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + nextptr = &multiview_features; + } + } + VkDeviceCreateInfo sdevice = { /*sType*/ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - /*pNext*/ nullptr, + /*pNext*/ nextptr, /*flags*/ 0, /*queueCreateInfoCount*/ 1, /*pQueueCreateInfos*/ queues, @@ -586,7 +1162,6 @@ Error VulkanContext::_create_device() { /*enabledExtensionCount*/ enabled_extension_count, /*ppEnabledExtensionNames*/ (const char *const *)extension_names, /*pEnabledFeatures*/ &physical_device_features, // If specific features are required, pass them in here - }; if (separate_present_queue) { queues[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -597,17 +1172,24 @@ Error VulkanContext::_create_device() { queues[1].flags = 0; sdevice.queueCreateInfoCount = 2; } - err = vkCreateDevice(gpu, &sdevice, nullptr, &device); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + + if (vulkan_hooks) { + if (!vulkan_hooks->create_vulkan_device(&sdevice, &device)) { + return ERR_CANT_CREATE; + } + } else { + err = vkCreateDevice(gpu, &sdevice, nullptr, &device); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + } return OK; } -Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) { +Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { // Iterate over each queue to learn whether it supports presenting: VkBool32 *supportsPresent = (VkBool32 *)malloc(queue_family_count * sizeof(VkBool32)); for (uint32_t i = 0; i < queue_family_count; i++) { - fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, surface, &supportsPresent[i]); + fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supportsPresent[i]); } // Search for a graphics and a present queue in the array of queue @@ -681,10 +1263,10 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) { // Get the list of VkFormat's that are supported: uint32_t formatCount; - VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, nullptr); + VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, nullptr); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); - err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, surfFormats); + err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, surfFormats); if (err) { free(surfFormats); ERR_FAIL_V(ERR_CANT_CREATE); @@ -692,16 +1274,39 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) { // If the format list includes just one entry of VK_FORMAT_UNDEFINED, // the surface has no preferred format. Otherwise, at least one // supported format will be returned. - if (true || (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)) { + if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { format = VK_FORMAT_B8G8R8A8_UNORM; + color_space = surfFormats[0].colorSpace; } else { + // These should be ordered with the ones we want to use on top and fallback modes further down + // we want an 32bit RGBA unsigned normalised buffer or similar + const VkFormat allowed_formats[] = { + VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_R8G8B8A8_UNORM + }; + uint32_t allowed_formats_count = sizeof(allowed_formats) / sizeof(VkFormat); + if (formatCount < 1) { free(surfFormats); ERR_FAIL_V_MSG(ERR_CANT_CREATE, "formatCount less than 1"); } - format = surfFormats[0].format; + + // Find the first format that we support + format = VK_FORMAT_UNDEFINED; + for (uint32_t af = 0; af < allowed_formats_count && format == VK_FORMAT_UNDEFINED; af++) { + for (uint32_t sf = 0; sf < formatCount && format == VK_FORMAT_UNDEFINED; sf++) { + if (surfFormats[sf].format == allowed_formats[af]) { + format = surfFormats[sf].format; + color_space = surfFormats[sf].colorSpace; + } + } + } + + if (format == VK_FORMAT_UNDEFINED) { + free(surfFormats); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "No usable surface format found."); + } } - color_space = surfFormats[0].colorSpace; free(surfFormats); @@ -736,9 +1341,6 @@ Error VulkanContext::_create_semaphores() { err = vkCreateFence(device, &fence_ci, nullptr, &fences[i]); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &image_acquired_semaphores[i]); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &draw_complete_semaphores[i]); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); @@ -755,9 +1357,18 @@ Error VulkanContext::_create_semaphores() { return OK; } -Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, VkSurfaceKHR p_surface, int p_width, int p_height) { +bool VulkanContext::_use_validation_layers() { + return Engine::get_singleton()->is_validation_layers_enabled(); +} + +Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height) { ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER); + if (!device_initialized) { + Error err = _create_physical_device(p_surface); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + } + if (!queues_initialized) { // We use a single GPU, but we need a surface to initialize the // queues, so this process must be deferred until a surface @@ -770,9 +1381,21 @@ Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, VkSurfa window.surface = p_surface; window.width = p_width; window.height = p_height; + window.vsync_mode = p_vsync_mode; Error err = _update_swap_chain(&window); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + VkSemaphoreCreateInfo semaphoreCreateInfo = { + /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + /*pNext*/ nullptr, + /*flags*/ 0, + }; + + for (uint32_t i = 0; i < FRAME_LAG; i++) { + VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window.image_acquired_semaphores[i]); + ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE); + } + windows[p_window_id] = window; return OK; } @@ -794,6 +1417,12 @@ int VulkanContext::window_get_height(DisplayServer::WindowID p_window) { return windows[p_window].height; } +bool VulkanContext::window_is_valid_swapchain(DisplayServer::WindowID p_window) { + ERR_FAIL_COND_V(!windows.has(p_window), false); + Window *w = &windows[p_window]; + return w->swapchain_image_resources != VK_NULL_HANDLE; +} + VkRenderPass VulkanContext::window_get_render_pass(DisplayServer::WindowID p_window) { ERR_FAIL_COND_V(!windows.has(p_window), VK_NULL_HANDLE); Window *w = &windows[p_window]; @@ -806,12 +1435,20 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi ERR_FAIL_COND_V(!buffers_prepared, VK_NULL_HANDLE); Window *w = &windows[p_window]; //vulkan use of currentbuffer - return w->swapchain_image_resources[w->current_buffer].framebuffer; + if (w->swapchain_image_resources != VK_NULL_HANDLE) { + return w->swapchain_image_resources[w->current_buffer].framebuffer; + } else { + return VK_NULL_HANDLE; + } } void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) { ERR_FAIL_COND(!windows.has(p_window_id)); _clean_up_swap_chain(&windows[p_window_id]); + for (uint32_t i = 0; i < FRAME_LAG; i++) { + vkDestroySemaphore(device, windows[p_window_id].image_acquired_semaphores[i], nullptr); + } + vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr); windows.erase(p_window_id); } @@ -898,7 +1535,6 @@ Error VulkanContext::_update_swap_chain(Window *window) { } // The FIFO present mode is guaranteed by the spec to be supported // and to have no tearing. It's a great default present mode to use. - VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; // There are times when you may wish to use another present mode. The // following code shows how to select them, and the comments provide some @@ -927,16 +1563,41 @@ Error VulkanContext::_update_swap_chain(Window *window) { // the application wants the late image to be immediately displayed, even // though that may mean some tearing. - if (window->presentMode != swapchainPresentMode) { - for (size_t i = 0; i < presentModeCount; ++i) { - if (presentModes[i] == window->presentMode) { - swapchainPresentMode = window->presentMode; - break; - } + VkPresentModeKHR requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; + switch (window->vsync_mode) { + case DisplayServer::VSYNC_MAILBOX: + requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR; + break; + case DisplayServer::VSYNC_ADAPTIVE: + requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_RELAXED_KHR; + break; + case DisplayServer::VSYNC_ENABLED: + requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; + break; + case DisplayServer::VSYNC_DISABLED: + requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_IMMEDIATE_KHR; + break; + } + + // Check if the requested mode is available. + bool present_mode_available = false; + for (uint32_t i = 0; i < presentModeCount; i++) { + if (presentModes[i] == requested_present_mode) { + present_mode_available = true; } } + + // Set the windows present mode if it is available, otherwise FIFO is used (guaranteed supported). + if (present_mode_available) { + window->presentMode = requested_present_mode; + } else { + WARN_PRINT("Requested VSync mode is not available!"); + window->vsync_mode = DisplayServer::VSYNC_ENABLED; //Set to default + } + + print_verbose("Using present mode: " + String(string_VkPresentModeKHR(window->presentMode))); + free(presentModes); - ERR_FAIL_COND_V_MSG(swapchainPresentMode != window->presentMode, ERR_CANT_CREATE, "Present mode specified is not supported\n"); // Determine the number of VkImages to use in the swap chain. // Application desires to acquire 3 images at a time for triple @@ -993,7 +1654,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*pQueueFamilyIndices*/ nullptr, /*preTransform*/ (VkSurfaceTransformFlagBitsKHR)preTransform, /*compositeAlpha*/ compositeAlpha, - /*presentMode*/ swapchainPresentMode, + /*presentMode*/ window->presentMode, /*clipped*/ true, /*oldSwapchain*/ VK_NULL_HANDLE, }; @@ -1185,12 +1846,17 @@ Error VulkanContext::_update_swap_chain(Window *window) { } Error VulkanContext::initialize() { - Error err = _create_physical_device(); - if (err) { +#ifdef USE_VOLK + if (volkInitialize() != VK_SUCCESS) { + return FAILED; + } +#endif + + Error err = _create_instance(); + if (err != OK) { return err; } - device_initialized = true; return OK; } @@ -1263,8 +1929,10 @@ Error VulkanContext::prepare_buffers() { vkWaitForFences(device, 1, &fences[frame_index], VK_TRUE, UINT64_MAX); vkResetFences(device, 1, &fences[frame_index]); - for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) { - Window *w = &E->get(); + for (KeyValue<int, Window> &E : windows) { + Window *w = &E.value; + + w->semaphore_acquired = false; if (w->swapchain == VK_NULL_HANDLE) { continue; @@ -1274,21 +1942,23 @@ Error VulkanContext::prepare_buffers() { // Get the index of the next available swapchain image: err = fpAcquireNextImageKHR(device, w->swapchain, UINT64_MAX, - image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); + w->image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); if (err == VK_ERROR_OUT_OF_DATE_KHR) { // swapchain is out of date (e.g. the window was resized) and // must be recreated: - print_line("early out of data"); + print_verbose("Vulkan: Early out of date swapchain, recreating."); //resize_notify(); _update_swap_chain(w); } else if (err == VK_SUBOPTIMAL_KHR) { - print_line("early suboptimal"); // swapchain is not as optimal as it could be, but the platform's // presentation engine will still present the image correctly. + print_verbose("Vulkan: Early suboptimal swapchain."); break; + } else if (err != VK_SUCCESS) { + ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully."); } else { - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + w->semaphore_acquired = true; } } while (err != VK_SUCCESS); } @@ -1313,13 +1983,13 @@ Error VulkanContext::swap_buffers() { DemoUpdateTargetIPD(demo); // Note: a real application would position its geometry to that it's in - // the correct locatoin for when the next image is presented. It might + // the correct location for when the next image is presented. It might // also wait, so that there's less latency between any input and when // the next image is rendered/presented. This demo program is so // simple that it doesn't do either of those. } #endif - // Wait for the image acquired semaphore to be signaled to ensure + // Wait for the image acquired semaphore to be signalled to ensure // that the image won't be rendered to until the presentation // engine has fully released ownership to the application, and it is // okay to render to the image. @@ -1338,14 +2008,26 @@ Error VulkanContext::swap_buffers() { commands_to_submit = command_buffer_count; } - VkPipelineStageFlags pipe_stage_flags; + VkSemaphore *semaphores_to_acquire = (VkSemaphore *)alloca(windows.size() * sizeof(VkSemaphore)); + VkPipelineStageFlags *pipe_stage_flags = (VkPipelineStageFlags *)alloca(windows.size() * sizeof(VkPipelineStageFlags)); + uint32_t semaphores_to_acquire_count = 0; + + for (KeyValue<int, Window> &E : windows) { + Window *w = &E.value; + + if (w->semaphore_acquired) { + semaphores_to_acquire[semaphores_to_acquire_count] = w->image_acquired_semaphores[frame_index]; + pipe_stage_flags[semaphores_to_acquire_count] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + semaphores_to_acquire_count++; + } + } + VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; - submit_info.pWaitDstStageMask = &pipe_stage_flags; - pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &image_acquired_semaphores[frame_index]; + submit_info.waitSemaphoreCount = semaphores_to_acquire_count; + submit_info.pWaitSemaphores = semaphores_to_acquire; + submit_info.pWaitDstStageMask = pipe_stage_flags; submit_info.commandBufferCount = commands_to_submit; submit_info.pCommandBuffers = commands_ptr; submit_info.signalSemaphoreCount = 1; @@ -1361,7 +2043,7 @@ Error VulkanContext::swap_buffers() { // present queue before presenting, waiting for the draw complete // semaphore and signalling the ownership released semaphore when finished VkFence nullFence = VK_NULL_HANDLE; - pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + pipe_stage_flags[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &draw_complete_semaphores[frame_index]; submit_info.commandBufferCount = 0; @@ -1369,8 +2051,8 @@ Error VulkanContext::swap_buffers() { VkCommandBuffer *cmdbufptr = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer *) * windows.size()); submit_info.pCommandBuffers = cmdbufptr; - for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) { - Window *w = &E->get(); + for (KeyValue<int, Window> &E : windows) { + Window *w = &E.value; if (w->swapchain == VK_NULL_HANDLE) { continue; @@ -1385,7 +2067,7 @@ Error VulkanContext::swap_buffers() { ERR_FAIL_COND_V(err, ERR_CANT_CREATE); } - // If we are using separate queues we have to wait for image ownership, + // If we are using separate queues, we have to wait for image ownership, // otherwise wait for draw complete VkPresentInfoKHR present = { /*sType*/ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, @@ -1404,8 +2086,8 @@ Error VulkanContext::swap_buffers() { present.pSwapchains = pSwapchains; present.pImageIndices = pImageIndices; - for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) { - Window *w = &E->get(); + for (KeyValue<int, Window> &E : windows) { + Window *w = &E.value; if (w->swapchain == VK_NULL_HANDLE) { continue; @@ -1493,12 +2175,12 @@ Error VulkanContext::swap_buffers() { if (err == VK_ERROR_OUT_OF_DATE_KHR) { // swapchain is out of date (e.g. the window was resized) and // must be recreated: - print_line("out of date"); + print_verbose("Vulkan: Swapchain is out of date, recreating."); resize_notify(); } else if (err == VK_SUBOPTIMAL_KHR) { // swapchain is not as optimal as it could be, but the platform's // presentation engine will still present the image correctly. - print_line("suboptimal"); + print_verbose("Vulkan: Swapchain is suboptimal."); } else { ERR_FAIL_COND_V(err, ERR_CANT_CREATE); } @@ -1522,7 +2204,11 @@ int VulkanContext::get_swapchain_image_count() const { return swapchainImageCount; } -uint32_t VulkanContext::get_graphics_queue() const { +VkQueue VulkanContext::get_graphics_queue() const { + return graphics_queue; +} + +uint32_t VulkanContext::get_graphics_queue_family_index() const { return graphics_queue_family_index; } @@ -1573,12 +2259,12 @@ RID VulkanContext::local_device_create() { } VkDevice VulkanContext::local_device_get_vk_device(RID p_local_device) { - LocalDevice *ld = local_device_owner.getornull(p_local_device); + LocalDevice *ld = local_device_owner.get_or_null(p_local_device); return ld->device; } void VulkanContext::local_device_push_command_buffers(RID p_local_device, const VkCommandBuffer *p_buffers, int p_count) { - LocalDevice *ld = local_device_owner.getornull(p_local_device); + LocalDevice *ld = local_device_owner.get_or_null(p_local_device); ERR_FAIL_COND(ld->waiting); VkSubmitInfo submit_info; @@ -1594,13 +2280,13 @@ void VulkanContext::local_device_push_command_buffers(RID p_local_device, const VkResult err = vkQueueSubmit(ld->queue, 1, &submit_info, VK_NULL_HANDLE); if (err == VK_ERROR_OUT_OF_HOST_MEMORY) { - print_line("out of host memory"); + print_line("Vulkan: Out of host memory!"); } if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) { - print_line("out of device memory"); + print_line("Vulkan: Out of device memory!"); } if (err == VK_ERROR_DEVICE_LOST) { - print_line("device lost"); + print_line("Vulkan: Device lost!"); } ERR_FAIL_COND(err); @@ -1608,7 +2294,7 @@ void VulkanContext::local_device_push_command_buffers(RID p_local_device, const } void VulkanContext::local_device_sync(RID p_local_device) { - LocalDevice *ld = local_device_owner.getornull(p_local_device); + LocalDevice *ld = local_device_owner.get_or_null(p_local_device); ERR_FAIL_COND(!ld->waiting); vkDeviceWaitIdle(ld->device); @@ -1616,7 +2302,7 @@ void VulkanContext::local_device_sync(RID p_local_device) { } void VulkanContext::local_device_free(RID p_local_device) { - LocalDevice *ld = local_device_owner.getornull(p_local_device); + LocalDevice *ld = local_device_owner.get_or_null(p_local_device); vkDestroyDevice(ld->device, nullptr); local_device_owner.free(p_local_device); } @@ -1681,13 +2367,27 @@ String VulkanContext::get_device_vendor_name() const { String VulkanContext::get_device_name() const { return device_name; } + +RenderingDevice::DeviceType VulkanContext::get_device_type() const { + return RenderingDevice::DeviceType(device_type); +} + String VulkanContext::get_device_pipeline_cache_uuid() const { return pipeline_cache_id; } -VulkanContext::VulkanContext() { - use_validation_layers = Engine::get_singleton()->is_validation_layers_enabled(); +DisplayServer::VSyncMode VulkanContext::get_vsync_mode(DisplayServer::WindowID p_window) const { + ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + return windows[p_window].vsync_mode; +} + +void VulkanContext::set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) { + ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + windows[p_window].vsync_mode = p_mode; + _update_swap_chain(&windows[p_window]); +} +VulkanContext::VulkanContext() { command_buffer_queue.resize(1); // First one is always the setup command. command_buffer_queue.write[0] = nullptr; } @@ -1699,15 +2399,17 @@ VulkanContext::~VulkanContext() { if (device_initialized) { for (uint32_t i = 0; i < FRAME_LAG; i++) { vkDestroyFence(device, fences[i], nullptr); - vkDestroySemaphore(device, image_acquired_semaphores[i], nullptr); vkDestroySemaphore(device, draw_complete_semaphores[i], nullptr); if (separate_present_queue) { vkDestroySemaphore(device, image_ownership_semaphores[i], nullptr); } } - if (inst_initialized && use_validation_layers) { + if (inst_initialized && enabled_debug_utils) { DestroyDebugUtilsMessengerEXT(inst, dbg_messenger, nullptr); } + if (inst_initialized && dbg_debug_report != VK_NULL_HANDLE) { + DestroyDebugReportCallbackEXT(inst, dbg_debug_report, nullptr); + } vkDestroyDevice(device, nullptr); } if (inst_initialized) { diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index dc6b0410bc..8c0111714c 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,18 +37,59 @@ #include "core/templates/map.h" #include "core/templates/rid_owner.h" #include "servers/display_server.h" +#include "servers/rendering/rendering_device.h" +#ifdef USE_VOLK +#include <volk.h> +#else #include <vulkan/vulkan.h> +#endif + +#include "vulkan_hooks.h" class VulkanContext { +public: + struct SubgroupCapabilities { + uint32_t size; + VkShaderStageFlags supportedStages; + VkSubgroupFeatureFlags supportedOperations; + VkBool32 quadOperationsInAllStages; + + uint32_t supported_stages_flags_rd() const; + String supported_stages_desc() const; + uint32_t supported_operations_flags_rd() const; + String supported_operations_desc() const; + }; + + struct MultiviewCapabilities { + bool is_supported; + bool geometry_shader_is_supported; + bool tessellation_shader_is_supported; + uint32_t max_view_count; + uint32_t max_instance_count; + }; + + struct ShaderCapabilities { + bool shader_float16_is_supported; + bool shader_int8_is_supported; + }; + + struct StorageBufferCapabilities { + bool storage_buffer_16_bit_access_is_supported; + bool uniform_and_storage_buffer_16_bit_access_is_supported; + bool storage_push_constant_16_is_supported; + bool storage_input_output_16; + }; + +private: enum { MAX_EXTENSIONS = 128, MAX_LAYERS = 64, FRAME_LAG = 2 }; + static VulkanHooks *vulkan_hooks; VkInstance inst = VK_NULL_HANDLE; - VkSurfaceKHR surface = VK_NULL_HANDLE; VkPhysicalDevice gpu = VK_NULL_HANDLE; VkPhysicalDeviceProperties gpu_props; uint32_t queue_family_count = 0; @@ -57,8 +98,18 @@ class VulkanContext { bool device_initialized = false; bool inst_initialized = false; + // Vulkan 1.0 doesn't return version info so we assume this by default until we know otherwise + uint32_t vulkan_major = 1; + uint32_t vulkan_minor = 0; + uint32_t vulkan_patch = 0; + SubgroupCapabilities subgroup_capabilities; + MultiviewCapabilities multiview_capabilities; + ShaderCapabilities shader_capabilities; + StorageBufferCapabilities storage_buffer_capabilities; + String device_vendor; String device_name; + VkPhysicalDeviceType device_type; String pipeline_cache_id; uint32_t device_api_version = 0; @@ -73,7 +124,6 @@ class VulkanContext { VkQueue present_queue = VK_NULL_HANDLE; VkColorSpaceKHR color_space; VkFormat format; - VkSemaphore image_acquired_semaphores[FRAME_LAG]; VkSemaphore draw_complete_semaphores[FRAME_LAG]; VkSemaphore image_ownership_semaphores[FRAME_LAG]; int frame_index = 0; @@ -93,9 +143,12 @@ class VulkanContext { VkSwapchainKHR swapchain = VK_NULL_HANDLE; SwapchainImageResources *swapchain_image_resources = VK_NULL_HANDLE; VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + VkSemaphore image_acquired_semaphores[FRAME_LAG]; + bool semaphore_acquired = false; uint32_t current_buffer = 0; int width = 0; int height = 0; + DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; VkCommandPool present_cmd_pool = VK_NULL_HANDLE; // For separate present queue. VkRenderPass render_pass = VK_NULL_HANDLE; }; @@ -126,8 +179,11 @@ class VulkanContext { const char *extension_names[MAX_EXTENSIONS]; bool enabled_debug_utils = false; - uint32_t enabled_layer_count = 0; - const char *enabled_layers[MAX_LAYERS]; + /** + * True if VK_EXT_debug_report extension is used. VK_EXT_debug_report is deprecated but it is + * still used if VK_EXT_debug_utils is not available. + */ + bool enabled_debug_report = false; PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT; PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT; @@ -136,6 +192,9 @@ class VulkanContext { PFN_vkCmdEndDebugUtilsLabelEXT CmdEndDebugUtilsLabelEXT; PFN_vkCmdInsertDebugUtilsLabelEXT CmdInsertDebugUtilsLabelEXT; PFN_vkSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT; + PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; + PFN_vkDebugReportMessageEXT DebugReportMessageEXT; + PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR; @@ -149,20 +208,34 @@ class VulkanContext { PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE; VkDebugUtilsMessengerEXT dbg_messenger = VK_NULL_HANDLE; + VkDebugReportCallbackEXT dbg_debug_report = VK_NULL_HANDLE; - Error _create_validation_layers(); + Error _obtain_vulkan_version(); Error _initialize_extensions(); + Error _check_capabilities(); - VkBool32 _check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers); + VkBool32 _check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers); static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData); - Error _create_physical_device(); + static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_report_callback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char *pLayerPrefix, + const char *pMessage, + void *pUserData); + + Error _create_instance(); - Error _initialize_queues(VkSurfaceKHR surface); + Error _create_physical_device(VkSurfaceKHR p_surface); + + Error _initialize_queues(VkSurfaceKHR p_surface); Error _create_device(); @@ -176,24 +249,33 @@ class VulkanContext { protected: virtual const char *_get_platform_surface_extension() const = 0; - // Enabled via command line argument. - bool use_validation_layers = false; + virtual Error _window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height); - virtual Error _window_create(DisplayServer::WindowID p_window_id, VkSurfaceKHR p_surface, int p_width, int p_height); + virtual bool _use_validation_layers(); - VkInstance _get_instance() { - return inst; - } + Error _get_preferred_validation_layers(uint32_t *count, const char *const **names); public: + uint32_t get_vulkan_major() const { return vulkan_major; }; + uint32_t get_vulkan_minor() const { return vulkan_minor; }; + SubgroupCapabilities get_subgroup_capabilities() const { return subgroup_capabilities; }; + MultiviewCapabilities get_multiview_capabilities() const { return multiview_capabilities; }; + ShaderCapabilities get_shader_capabilities() const { return shader_capabilities; }; + StorageBufferCapabilities get_storage_buffer_capabilities() const { return storage_buffer_capabilities; }; + VkDevice get_device(); VkPhysicalDevice get_physical_device(); + VkInstance get_instance() { return inst; } int get_swapchain_image_count() const; - uint32_t get_graphics_queue() const; + VkQueue get_graphics_queue() const; + uint32_t get_graphics_queue_family_index() const; + + static void set_vulkan_hooks(VulkanHooks *p_vulkan_hooks) { vulkan_hooks = p_vulkan_hooks; }; void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); int window_get_width(DisplayServer::WindowID p_window = 0); int window_get_height(DisplayServer::WindowID p_window = 0); + bool window_is_valid_swapchain(DisplayServer::WindowID p_window = 0); void window_destroy(DisplayServer::WindowID p_window_id); VkFramebuffer window_get_framebuffer(DisplayServer::WindowID p_window = 0); VkRenderPass window_get_render_pass(DisplayServer::WindowID p_window = 0); @@ -222,8 +304,12 @@ public: String get_device_vendor_name() const; String get_device_name() const; + RenderingDevice::DeviceType get_device_type() const; String get_device_pipeline_cache_uuid() const; + void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode); + DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const; + VulkanContext(); virtual ~VulkanContext(); }; diff --git a/drivers/vulkan/vulkan_hooks.h b/drivers/vulkan/vulkan_hooks.h new file mode 100644 index 0000000000..3f244b4d45 --- /dev/null +++ b/drivers/vulkan/vulkan_hooks.h @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* vulkan_hooks.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 VULKAN_HOOKS_H +#define VULKAN_HOOKS_H + +#ifdef USE_VOLK +#include <volk.h> +#else +#include <vulkan/vulkan.h> +#endif + +class VulkanHooks { +public: + virtual bool create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) { return false; }; + virtual bool get_physical_device(VkPhysicalDevice *r_device) { return false; }; + virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) { return false; }; + virtual ~VulkanHooks(){}; +}; + +#endif diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 43c8722b06..c9609b469a 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,8 +35,60 @@ #include "core/config/project_settings.h" #include "core/os/os.h" +#include <stdint.h> // INT32_MAX + #include <functiondiscoverykeys.h> +// Define IAudioClient3 if not already defined by MinGW headers +#if defined __MINGW32__ || defined __MINGW64__ + +#ifndef __IAudioClient3_FWD_DEFINED__ +#define __IAudioClient3_FWD_DEFINED__ + +typedef interface IAudioClient3 IAudioClient3; + +#endif // __IAudioClient3_FWD_DEFINED__ + +#ifndef __IAudioClient3_INTERFACE_DEFINED__ +#define __IAudioClient3_INTERFACE_DEFINED__ + +MIDL_INTERFACE("7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42") +IAudioClient3 : public IAudioClient2 { +public: + virtual HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod( + /* [annotation][in] */ + _In_ const WAVEFORMATEX *pFormat, + /* [annotation][out] */ + _Out_ UINT32 *pDefaultPeriodInFrames, + /* [annotation][out] */ + _Out_ UINT32 *pFundamentalPeriodInFrames, + /* [annotation][out] */ + _Out_ UINT32 *pMinPeriodInFrames, + /* [annotation][out] */ + _Out_ UINT32 *pMaxPeriodInFrames) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod( + /* [unique][annotation][out] */ + _Out_ WAVEFORMATEX * *ppFormat, + /* [annotation][out] */ + _Out_ UINT32 * pCurrentPeriodInFrames) = 0; + + virtual HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream( + /* [annotation][in] */ + _In_ DWORD StreamFlags, + /* [annotation][in] */ + _In_ UINT32 PeriodInFrames, + /* [annotation][in] */ + _In_ const WAVEFORMATEX *pFormat, + /* [annotation][in] */ + _In_opt_ LPCGUID AudioSessionGuid) = 0; +}; +__CRT_UUID_DECL(IAudioClient3, 0x7ED4EE07, 0x8E67, 0x4CD4, 0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42) + +#endif // __IAudioClient3_INTERFACE_DEFINED__ + +#endif // __MINGW32__ || __MINGW64__ + #ifndef PKEY_Device_FriendlyName #undef DEFINE_PROPERTYKEY @@ -51,6 +103,7 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0 const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); +const IID IID_IAudioClient3 = __uuidof(IAudioClient3); const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); @@ -68,6 +121,12 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); static bool default_render_device_changed = false; static bool default_capture_device_changed = false; +// Silence warning due to a COM API weirdness (GH-35194). +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + class CMMNotificationClient : public IMMNotificationClient { LONG _cRef = 1; IMMDeviceEnumerator *_pEnumerator = nullptr; @@ -109,7 +168,7 @@ public: HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) { return S_OK; - }; + } HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) { return S_OK; @@ -136,6 +195,10 @@ public: } }; +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + static CMMNotificationClient notif_client; Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) { @@ -221,7 +284,22 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } - hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input) + if (using_audio_client_3) { + hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + if (hr != S_OK) { + // IID_IAudioClient3 will never activate on OS versions before Windows 10. + // Older Windows versions should fall back gracefully. + using_audio_client_3 = false; + print_verbose("WASAPI: Couldn't activate device with IAudioClient3 interface, falling back to IAudioClient interface"); + } else { + print_verbose("WASAPI: Activated device using IAudioClient3 interface"); + } + } + if (!using_audio_client_3) { + hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + } + SAFE_RELEASE(device) if (reinit) { @@ -232,6 +310,16 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } + if (using_audio_client_3) { + AudioClientProperties audioProps{}; + audioProps.cbSize = sizeof(AudioClientProperties); + audioProps.bIsOffload = FALSE; + audioProps.eCategory = AudioCategory_GameEffects; + + hr = ((IAudioClient3 *)p_device->audio_client)->SetClientProperties(&audioProps); + ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: SetClientProperties failed with error 0x" + String::num_uint64(hr, 16) + "."); + } + hr = p_device->audio_client->GetMixFormat(&pwfex); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); @@ -285,15 +373,70 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } } - DWORD streamflags = 0; - if ((DWORD)mix_rate != pwfex->nSamplesPerSec) { - streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST; - pwfex->nSamplesPerSec = mix_rate; - pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); - } + if (!using_audio_client_3) { + DWORD streamflags = 0; + if ((DWORD)mix_rate != pwfex->nSamplesPerSec) { + streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST; + pwfex->nSamplesPerSec = mix_rate; + pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); + } + hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr); + ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + "."); + UINT32 max_frames; + hr = p_device->audio_client->GetBufferSize(&max_frames); + ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + + // Due to WASAPI Shared Mode we have no control of the buffer size + if (!p_capture) { + buffer_frames = max_frames; - hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr); - ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + "."); + int64_t latency = 0; + audio_output.audio_client->GetStreamLatency(&latency); + // WASAPI REFERENCE_TIME units are 100 nanoseconds per unit + // https://docs.microsoft.com/en-us/windows/win32/directshow/reference-time + // Convert REFTIME to seconds as godot uses for latency + real_latency = (float)latency / (float)REFTIMES_PER_SEC; + } + + } else { + IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client; + + // AUDCLNT_STREAMFLAGS_RATEADJUST is an invalid flag with IAudioClient3, therefore we have to use + // the closest supported mix rate supported by the audio driver. + mix_rate = pwfex->nSamplesPerSec; + print_verbose("WASAPI: mix_rate = " + itos(mix_rate)); + + UINT32 default_period_frames, fundamental_period_frames, min_period_frames, max_period_frames; + hr = device_audio_client_3->GetSharedModeEnginePeriod( + pwfex, + &default_period_frames, + &fundamental_period_frames, + &min_period_frames, + &max_period_frames); + ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + "."); + + // Period frames must be an integral multiple of fundamental_period_frames or IAudioClient3 initialization will fail, + // so we need to select the closest multiple to the user-specified latency. + UINT32 desired_period_frames = target_latency_ms * mix_rate / 1000; + UINT32 period_frames = (desired_period_frames / fundamental_period_frames) * fundamental_period_frames; + if (ABS((int64_t)period_frames - (int64_t)desired_period_frames) > ABS((int64_t)(period_frames + fundamental_period_frames) - (int64_t)desired_period_frames)) { + period_frames = period_frames + fundamental_period_frames; + } + period_frames = CLAMP(period_frames, min_period_frames, max_period_frames); + print_verbose("WASAPI: fundamental_period_frames = " + itos(fundamental_period_frames)); + print_verbose("WASAPI: min_period_frames = " + itos(min_period_frames)); + print_verbose("WASAPI: max_period_frames = " + itos(max_period_frames)); + print_verbose("WASAPI: selected a period frame size of " + itos(period_frames)); + buffer_frames = period_frames; + + hr = device_audio_client_3->InitializeSharedAudioStream(0, period_frames, pwfex, nullptr); + ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + "."); + uint32_t output_latency_in_frames; + WAVEFORMATEX *current_pwfex; + device_audio_client_3->GetCurrentSharedModeEnginePeriod(¤t_pwfex, &output_latency_in_frames); + real_latency = (float)output_latency_in_frames / (float)current_pwfex->nSamplesPerSec; + CoTaskMemFree(current_pwfex); + } if (p_capture) { hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client); @@ -311,8 +454,9 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c Error AudioDriverWASAPI::init_render_device(bool reinit) { Error err = audio_device_init(&audio_output, false, reinit); - if (err != OK) + if (err != OK) { return err; + } switch (audio_output.channels) { case 2: // Stereo @@ -328,13 +472,6 @@ Error AudioDriverWASAPI::init_render_device(bool reinit) { break; } - UINT32 max_frames; - HRESULT hr = audio_output.audio_client->GetBufferSize(&max_frames); - ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - - // Due to WASAPI Shared Mode we have no control of the buffer size - buffer_frames = max_frames; - // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) samples_in.resize(buffer_frames * channels); @@ -349,8 +486,9 @@ Error AudioDriverWASAPI::init_render_device(bool reinit) { Error AudioDriverWASAPI::init_capture_device(bool reinit) { Error err = audio_device_init(&audio_input, true, reinit); - if (err != OK) + if (err != OK) { return err; + } // Get the max frames UINT32 max_frames; @@ -367,7 +505,6 @@ Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) { if (p_device->audio_client) { p_device->audio_client->Stop(); } - p_device->active = false; } @@ -389,6 +526,8 @@ Error AudioDriverWASAPI::finish_capture_device() { Error AudioDriverWASAPI::init() { mix_rate = GLOBAL_GET("audio/driver/mix_rate"); + target_latency_ms = GLOBAL_GET("audio/driver/output_latency"); + Error err = init_render_device(); if (err != OK) { ERR_PRINT("WASAPI: init_render_device error"); @@ -406,6 +545,10 @@ int AudioDriverWASAPI::get_mix_rate() const { return mix_rate; } +float AudioDriverWASAPI::get_latency() { + return real_latency; +} + AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index b9b325f0fb..89ed90e97b 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,6 +39,7 @@ #include <audioclient.h> #include <mmdeviceapi.h> +#define WIN32_LEAN_AND_MEAN #include <windows.h> class AudioDriverWASAPI : public AudioDriver { @@ -71,6 +72,9 @@ class AudioDriverWASAPI : public AudioDriver { unsigned int channels = 0; int mix_rate = 0; int buffer_frames = 0; + int target_latency_ms = 0; + float real_latency = 0.0; + bool using_audio_client_3 = false; bool thread_exited = false; mutable bool exit_thread = false; @@ -97,6 +101,7 @@ public: virtual Error init(); virtual void start(); virtual int get_mix_rate() const; + virtual float get_latency(); virtual SpeakerMode get_speaker_mode() const; virtual Array get_device_list(); virtual String get_device(); @@ -114,5 +119,5 @@ public: AudioDriverWASAPI(); }; +#endif // WASAPI_ENABLED #endif // AUDIO_DRIVER_WASAPI_H -#endif diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 2c9f28717d..6f3bad12c1 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,7 @@ #include <stdio.h> #include <wchar.h> +#define WIN32_LEAN_AND_MEAN #include <windows.h> /* @@ -132,7 +133,7 @@ Error DirAccessWindows::change_dir(String p_dir) { bool worked = (SetCurrentDirectoryW((LPCWSTR)(p_dir.utf16().get_data())) != 0); String base = _get_root_path(); - if (base != "") { + if (!base.is_empty()) { GetCurrentDirectoryW(2048, real_current_dir_name); String new_dir = String::utf16((const char16_t *)real_current_dir_name).replace("\\", "/"); if (!new_dir.begins_with(base)) { @@ -155,7 +156,7 @@ Error DirAccessWindows::make_dir(String p_dir) { GLOBAL_LOCK_FUNCTION p_dir = fix_path(p_dir); - if (p_dir.is_rel_path()) { + if (p_dir.is_relative_path()) { p_dir = current_dir.plus_file(p_dir); } @@ -164,8 +165,11 @@ Error DirAccessWindows::make_dir(String p_dir) { bool success; int err; - p_dir = "\\\\?\\" + p_dir; //done according to - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx + if (!p_dir.is_network_share_path()) { + p_dir = "\\\\?\\" + p_dir; + // Add "\\?\" to the path to extend max. path length past 248, if it's not a network share UNC path. + // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx + } success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), nullptr); err = GetLastError(); @@ -183,7 +187,7 @@ Error DirAccessWindows::make_dir(String p_dir) { String DirAccessWindows::get_current_dir(bool p_include_drive) { String base = _get_root_path(); - if (base != "") { + if (!base.is_empty()) { String bd = current_dir.replace("\\", "/").replace_first(base, ""); if (bd.begins_with("/")) { return _get_root_string() + bd.substr(1, bd.length()); @@ -195,10 +199,10 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) { if (p_include_drive) { return current_dir; } else { - if (_get_root_string() == "") { + if (_get_root_string().is_empty()) { int p = current_dir.find(":"); if (p != -1) { - return current_dir.right(p + 1); + return current_dir.substr(p + 1); } } return current_dir; @@ -208,7 +212,7 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) { bool DirAccessWindows::file_exists(String p_file) { GLOBAL_LOCK_FUNCTION - if (!p_file.is_abs_path()) { + if (!p_file.is_absolute_path()) { p_file = get_current_dir().plus_file(p_file); } @@ -227,7 +231,7 @@ bool DirAccessWindows::file_exists(String p_file) { bool DirAccessWindows::dir_exists(String p_dir) { GLOBAL_LOCK_FUNCTION - if (p_dir.is_rel_path()) { + if (p_dir.is_relative_path()) { p_dir = get_current_dir().plus_file(p_dir); } @@ -242,13 +246,13 @@ bool DirAccessWindows::dir_exists(String p_dir) { } Error DirAccessWindows::rename(String p_path, String p_new_path) { - if (p_path.is_rel_path()) { + if (p_path.is_relative_path()) { p_path = get_current_dir().plus_file(p_path); } p_path = fix_path(p_path); - if (p_new_path.is_rel_path()) { + if (p_new_path.is_relative_path()) { p_new_path = get_current_dir().plus_file(p_new_path); } @@ -256,6 +260,11 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) { // If we're only changing file name case we need to do a little juggling if (p_path.to_lower() == p_new_path.to_lower()) { + if (dir_exists(p_path)) { + // The path is a dir; just rename + return ::_wrename((LPCWSTR)(p_path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED; + } + // The path is a file; juggle WCHAR tmpfile[MAX_PATH]; if (!GetTempFileNameW((LPCWSTR)(fix_path(get_current_dir()).utf16().get_data()), nullptr, 0, tmpfile)) { @@ -281,7 +290,7 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) { } Error DirAccessWindows::remove(String p_path) { - if (p_path.is_rel_path()) { + if (p_path.is_relative_path()) { p_path = get_current_dir().plus_file(p_path); } @@ -325,14 +334,15 @@ FileType DirAccessWindows::get_file_type(const String& p_file) const { } */ -size_t DirAccessWindows::get_space_left() { + +uint64_t DirAccessWindows::get_space_left() { uint64_t bytes = 0; if (!GetDiskFreeSpaceEx(nullptr, (PULARGE_INTEGER)&bytes, nullptr, nullptr)) { return 0; } //this is either 0 or a value in bytes. - return (size_t)bytes; + return bytes; } String DirAccessWindows::get_filesystem_type() const { @@ -342,6 +352,10 @@ String DirAccessWindows::get_filesystem_type() const { ERR_FAIL_COND_V(unit_end == -1, String()); String unit = path.substr(0, unit_end + 1) + "\\"; + if (path.is_network_share_path()) { + return "Network Share"; + } + WCHAR szVolumeName[100]; WCHAR szFileSystemName[10]; DWORD dwSerialNumber = 0; diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h index 7f10023470..78d37074e5 100644 --- a/drivers/windows/dir_access_windows.h +++ b/drivers/windows/dir_access_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,11 +33,7 @@ #ifdef WINDOWS_ENABLED -#include "core/os/dir_access.h" - -/** - @author Juan Linietsky <reduz@gmail.com> -*/ +#include "core/io/dir_access.h" struct DirAccessWindowsPrivate; @@ -78,8 +74,11 @@ public: virtual Error rename(String p_path, String p_new_path); virtual Error remove(String p_path); - //virtual FileType get_file_type() const; - size_t get_space_left(); + virtual bool is_link(String p_file) { return false; }; + virtual String read_link(String p_file) { return p_file; }; + virtual Error create_link(String p_source, String p_target) { return FAILED; }; + + uint64_t get_space_left(); virtual String get_filesystem_type() const; @@ -87,6 +86,6 @@ public: ~DirAccessWindows(); }; -#endif //WINDOWS_ENABLED +#endif // WINDOWS_ENABLED -#endif +#endif // DIR_ACCESS_WINDOWS_H diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 35f61c0623..59dc1d8e77 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,9 @@ #include "core/os/os.h" #include "core/string/print_string.h" +#include <share.h> // _SH_DENYNO #include <shlwapi.h> +#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <errno.h> @@ -77,15 +79,15 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { return ERR_INVALID_PARAMETER; } - /* pretty much every implementation that uses fopen as primary - backend supports utf8 encoding */ + /* Pretty much every implementation that uses fopen as primary + backend supports utf8 encoding. */ struct _stat st; if (_wstat((LPCWSTR)(path.utf16().get_data()), &st) == 0) { if (!S_ISREG(st.st_mode)) { return ERR_FILE_CANT_OPEN; } - }; + } #ifdef TOOLS_ENABLED // Windows is case insensitive, but all other platforms are sensitive to it @@ -97,7 +99,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { HANDLE f = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d); if (f != INVALID_HANDLE_VALUE) { String fname = String::utf16((const char16_t *)(d.cFileName)); - if (fname != String()) { + if (!fname.is_empty()) { String base_file = path.get_file(); if (base_file != fname && base_file.findn(fname) == 0) { WARN_PRINT("Case mismatch opening requested file '" + base_file + "', stored as '" + fname + "' in the filesystem. This file will not open when exported to other case-sensitive platforms."); @@ -108,15 +110,15 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { } #endif - if (is_backup_save_enabled() && p_mode_flags & WRITE && !(p_mode_flags & READ)) { + if (is_backup_save_enabled() && p_mode_flags == WRITE) { save_path = path; path = path + ".tmp"; } - errno_t errcode = _wfopen_s(&f, (LPCWSTR)(path.utf16().get_data()), mode_string); + f = _wfsopen((LPCWSTR)(path.utf16().get_data()), mode_string, _SH_DENYNO); if (f == nullptr) { - switch (errcode) { + switch (errno) { case ENOENT: { last_error = ERR_FILE_NOT_FOUND; } break; @@ -140,7 +142,7 @@ void FileAccessWindows::close() { fclose(f); f = nullptr; - if (save_path != "") { + if (!save_path.is_empty()) { bool rename_error = true; int attempts = 4; while (rename_error && attempts) { @@ -157,10 +159,10 @@ void FileAccessWindows::close() { #else if (!PathFileExistsW((LPCWSTR)(save_path.utf16().get_data()))) { #endif - //creating new file + // Creating new file rename_error = _wrename((LPCWSTR)((save_path + ".tmp").utf16().get_data()), (LPCWSTR)(save_path.utf16().get_data())) != 0; } else { - //atomic replace for existing file + // Atomic replace for existing file rename_error = !ReplaceFileW((LPCWSTR)(save_path.utf16().get_data()), (LPCWSTR)((save_path + ".tmp").utf16().get_data()), nullptr, 2 | 4, nullptr, nullptr); } if (rename_error) { @@ -193,10 +195,11 @@ bool FileAccessWindows::is_open() const { return (f != nullptr); } -void FileAccessWindows::seek(size_t p_position) { +void FileAccessWindows::seek(uint64_t p_position) { ERR_FAIL_COND(!f); + last_error = OK; - if (fseek(f, p_position, SEEK_SET)) { + if (_fseeki64(f, p_position, SEEK_SET)) { check_errors(); } prev_op = 0; @@ -204,28 +207,28 @@ void FileAccessWindows::seek(size_t p_position) { void FileAccessWindows::seek_end(int64_t p_position) { ERR_FAIL_COND(!f); - if (fseek(f, p_position, SEEK_END)) { + + if (_fseeki64(f, p_position, SEEK_END)) { check_errors(); } prev_op = 0; } -size_t FileAccessWindows::get_position() const { - size_t aux_position = 0; - aux_position = ftell(f); - if (!aux_position) { +uint64_t FileAccessWindows::get_position() const { + int64_t aux_position = _ftelli64(f); + if (aux_position < 0) { check_errors(); } return aux_position; } -size_t FileAccessWindows::get_len() const { +uint64_t FileAccessWindows::get_length() const { ERR_FAIL_COND_V(!f, 0); - size_t pos = get_position(); - fseek(f, 0, SEEK_END); - int size = get_position(); - fseek(f, pos, SEEK_SET); + uint64_t pos = get_position(); + _fseeki64(f, 0, SEEK_END); + uint64_t size = get_position(); + _fseeki64(f, pos, SEEK_SET); return size; } @@ -237,6 +240,7 @@ bool FileAccessWindows::eof_reached() const { uint8_t FileAccessWindows::get_8() const { ERR_FAIL_COND_V(!f, 0); + if (flags == READ_WRITE || flags == WRITE_READ) { if (prev_op == WRITE) { fflush(f); @@ -252,18 +256,20 @@ uint8_t FileAccessWindows::get_8() const { return b; } -int FileAccessWindows::get_buffer(uint8_t *p_dst, int p_length) const { +uint64_t FileAccessWindows::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(!f, -1); + if (flags == READ_WRITE || flags == WRITE_READ) { if (prev_op == WRITE) { fflush(f); } prev_op = READ; } - int read = fread(p_dst, 1, p_length, f); + uint64_t read = fread(p_dst, 1, p_length, f); check_errors(); return read; -}; +} Error FileAccessWindows::get_error() const { return last_error; @@ -271,6 +277,7 @@ Error FileAccessWindows::get_error() const { void FileAccessWindows::flush() { ERR_FAIL_COND(!f); + fflush(f); if (prev_op == WRITE) { prev_op = 0; @@ -279,6 +286,7 @@ void FileAccessWindows::flush() { void FileAccessWindows::store_8(uint8_t p_dest) { ERR_FAIL_COND(!f); + if (flags == READ_WRITE || flags == WRITE_READ) { if (prev_op == READ) { if (last_error != ERR_FILE_EOF) { @@ -290,8 +298,10 @@ void FileAccessWindows::store_8(uint8_t p_dest) { fwrite(&p_dest, 1, 1, f); } -void FileAccessWindows::store_buffer(const uint8_t *p_src, int p_length) { +void FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_COND(!f); + ERR_FAIL_COND(!p_src && p_length > 0); + if (flags == READ_WRITE || flags == WRITE_READ) { if (prev_op == READ) { if (last_error != ERR_FILE_EOF) { @@ -304,10 +314,8 @@ void FileAccessWindows::store_buffer(const uint8_t *p_src, int p_length) { } bool FileAccessWindows::file_exists(const String &p_name) { - FILE *g; - //printf("opening file %s\n", p_fname.utf8().get_data()); String filename = fix_path(p_name); - _wfopen_s(&g, (LPCWSTR)(filename.utf16().get_data()), L"rb"); + FILE *g = _wfsopen((LPCWSTR)(filename.utf16().get_data()), L"rb", _SH_DENYNO); if (g == nullptr) { return false; } else { @@ -318,8 +326,9 @@ bool FileAccessWindows::file_exists(const String &p_name) { uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { String file = fix_path(p_file); - if (file.ends_with("/") && file != "/") + if (file.ends_with("/") && file != "/") { file = file.substr(0, file.length() - 1); + } struct _stat st; int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st); @@ -327,7 +336,8 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { if (rv == 0) { return st.st_mtime; } else { - ERR_FAIL_V_MSG(0, "Failed to get modified time for: " + file + "."); + print_verbose("Failed to get modified time for: " + p_file + ""); + return 0; } } diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 507e0b2c20..93d37c3b5a 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,7 @@ #ifdef WINDOWS_ENABLED -#include "core/os/file_access.h" +#include "core/io/file_access.h" #include "core/os/memory.h" #include <stdio.h> @@ -56,21 +56,21 @@ public: virtual String get_path() const; /// returns the path for the current open file virtual String get_path_absolute() const; /// returns the absolute path for the current open file - virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek(uint64_t p_position); ///< seek to a given position virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file - virtual size_t get_position() const; ///< get position in the file - virtual size_t get_len() const; ///< get size of the file + virtual uint64_t get_position() const; ///< get position in the file + virtual uint64_t get_length() const; ///< get size of the file virtual bool eof_reached() const; ///< reading passed EOF virtual uint8_t get_8() const; ///< get a byte - virtual int get_buffer(uint8_t *p_dst, int p_length) const; + virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; virtual Error get_error() const; ///< get last error virtual void flush(); virtual void store_8(uint8_t p_dest); ///< store a byte - virtual void store_buffer(const uint8_t *p_src, int p_length); ///< store an array of bytes + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes virtual bool file_exists(const String &p_name); ///< return true if a file exists diff --git a/drivers/winmidi/midi_driver_winmidi.cpp b/drivers/winmidi/midi_driver_winmidi.cpp index 730d608bbf..64912dc3af 100644 --- a/drivers/winmidi/midi_driver_winmidi.cpp +++ b/drivers/winmidi/midi_driver_winmidi.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/winmidi/midi_driver_winmidi.h b/drivers/winmidi/midi_driver_winmidi.h index bb9a87d610..6572ba0c16 100644 --- a/drivers/winmidi/midi_driver_winmidi.h +++ b/drivers/winmidi/midi_driver_winmidi.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,7 @@ #include "core/templates/vector.h" #include <stdio.h> +#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <mmsystem.h> diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index 1c7bf5d6c6..03fdfda23d 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/drivers/xaudio2/audio_driver_xaudio2.h b/drivers/xaudio2/audio_driver_xaudio2.h index d3938a19d0..9072269a0e 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.h +++ b/drivers/xaudio2/audio_driver_xaudio2.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,6 +36,7 @@ #include "servers/audio_server.h" #include <mmsystem.h> +#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <wrl/client.h> #include <xaudio2.h> |