diff options
Diffstat (limited to 'drivers')
89 files changed, 16468 insertions, 761 deletions
diff --git a/drivers/SCsub b/drivers/SCsub index a7b21b855f..dd81fc645c 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -25,6 +25,9 @@ SConscript("winmidi/SCsub") # Graphics drivers if env["vulkan"]: SConscript("vulkan/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..f86c4d82ef 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 */ @@ -181,7 +181,7 @@ Error AudioDriverALSA::init() { } void AudioDriverALSA::thread_func(void *p_udata) { - AudioDriverALSA *ad = (AudioDriverALSA *)p_udata; + AudioDriverALSA *ad = static_cast<AudioDriverALSA *>(p_udata); while (!ad->exit_thread) { ad->lock(); @@ -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..c334146dd2 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 */ @@ -73,7 +73,7 @@ static int get_message_size(uint8_t message) { } void MIDIDriverALSAMidi::thread_func(void *p_udata) { - MIDIDriverALSAMidi *md = (MIDIDriverALSAMidi *)p_udata; + MIDIDriverALSAMidi *md = static_cast<MIDIDriverALSAMidi *>(p_udata); uint64_t timestamp = 0; uint8_t buffer[256]; int expected_size = 255; 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 4139727422..276e69e470 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 */ @@ -42,7 +42,7 @@ OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) { - AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; + AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData); // If our selected device is the Default call set_device to update the // kAudioOutputUnitProperty_CurrentDevice property @@ -56,7 +56,7 @@ OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) { - AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; + AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData); // If our selected device is the Default call set_device to update the // kAudioOutputUnitProperty_CurrentDevice property @@ -168,15 +168,15 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { - AudioDriverCoreAudio *ad = (AudioDriverCoreAudio *)inRefCon; + AudioDriverCoreAudio *ad = static_cast<AudioDriverCoreAudio *>(inRefCon); if (!ad->active || !ad->try_lock()) { for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { AudioBuffer *abuf = &ioData->mBuffers[i]; memset(abuf->mData, 0, abuf->mDataByteSize); - }; + } return 0; - }; + } ad->start_counting_ticks(); @@ -195,21 +195,21 @@ 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, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { - AudioDriverCoreAudio *ad = (AudioDriverCoreAudio *)inRefCon; + AudioDriverCoreAudio *ad = static_cast<AudioDriverCoreAudio *>(inRefCon); if (!ad->active) { return 0; } @@ -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; @@ -521,8 +521,9 @@ Array AudioDriverCoreAudio::_get_device_list(bool capture) { 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; + } memfree(bufferList); @@ -540,7 +541,7 @@ Array AudioDriverCoreAudio::_get_device_list(bool capture) { 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]) + ")"); } memfree(buffer); @@ -579,8 +580,9 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { 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; + } memfree(bufferList); @@ -597,7 +599,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { 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; 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/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..1ddaf3d6a7 --- /dev/null +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -0,0 +1,1491 @@ +/*************************************************************************/ +/* 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" +#include "storage/material_storage.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) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + 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; + GLES3::Material *material_ptr = material_storage->get_material(material); + + if (material.is_null() && ci->canvas_group != nullptr) { + material = default_canvas_group_material; + } + + if (material != prev_material) { + GLES3::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].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) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + 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() { + GLES3::CanvasTextureStorage *canvas_texture_storage = GLES3::CanvasTextureStorage::get_singleton(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::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 = material_storage->shader_allocate(); + material_storage->shader_initialize(default_canvas_group_shader); + + material_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 = material_storage->material_allocate(); + material_storage->material_initialize(default_canvas_group_material); + + 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::singleton = nullptr; + +RasterizerCanvasGLES3 *RasterizerCanvasGLES3::get_singleton() { + return singleton; +} + +RasterizerCanvasGLES3::RasterizerCanvasGLES3() { + singleton = this; +} + +RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { + GLES3::CanvasTextureStorage *canvas_texture_storage = GLES3::CanvasTextureStorage::get_singleton(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + + state.canvas_shader.version_free(state.canvas_shader_default_version); + material_storage->material_free(default_canvas_group_material); + material_storage->shader_free(default_canvas_group_shader); + canvas_texture_storage->canvas_texture_free(default_canvas_texture); + singleton = nullptr; +} + +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..70066c5e2a --- /dev/null +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -0,0 +1,288 @@ +/*************************************************************************/ +/* 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/material_storage.h" +#include "storage/texture_storage.h" + +#include "shaders/canvas.glsl.gen.h" + +class RasterizerSceneGLES3; + +class RasterizerCanvasGLES3 : public RendererCanvasRender { + static RasterizerCanvasGLES3 *singleton; + + _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); + _FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3); + + _FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4); + _FORCE_INLINE_ void _update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4); + + enum { + 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 = nullptr; + 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 = nullptr; + 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 = nullptr; + + RasterizerStorageGLES3 *storage = nullptr; + + 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(); + + static RasterizerCanvasGLES3 *get_singleton(); + 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..61aefc1192 --- /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..2279a502a2 --- /dev/null +++ b/drivers/gles3/rasterizer_gles3.h @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* 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/material_storage.h" +#include "storage/mesh_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; + GLES3::MaterialStorage material_storage; + GLES3::MeshStorage mesh_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; } + RendererMaterialStorage *get_material_storage() { return &material_storage; } + RendererMeshStorage *get_mesh_storage() { return &mesh_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..3517d985f0 --- /dev/null +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -0,0 +1,2114 @@ +/*************************************************************************/ +/* 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) { +} + +/* 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::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; +} + +/* 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() { +} + +bool RasterizerStorageGLES3::particles_is_inactive(RID p_particles) const { + return false; +} + +/* 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) { +} + +/* 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 (GLES3::MaterialStorage::get_singleton()->owns_shader(p_rid)) { + GLES3::MaterialStorage::get_singleton()->shader_free(p_rid); + return true; + } else if (GLES3::MaterialStorage::get_singleton()->owns_material(p_rid)) { + GLES3::MaterialStorage::get_singleton()->material_free(p_rid); + return true; + } else { + 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); + + { + // 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() { + GLES3::MaterialStorage::get_singleton()->update_dirty_shaders(); + GLES3::MaterialStorage::get_singleton()->update_dirty_materials(); + // update_dirty_skeletons(); + // update_dirty_multimeshes(); +} + +RasterizerStorageGLES3::RasterizerStorageGLES3() { + RasterizerStorageGLES3::system_fbo = 0; +} + +RasterizerStorageGLES3::~RasterizerStorageGLES3() { +} + +#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..105529ee3d --- /dev/null +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -0,0 +1,572 @@ +/*************************************************************************/ +/* 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/material_storage.h" +#include "storage/render_target_storage.h" +#include "storage/texture_storage.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; + + 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); + + /* 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; + 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; + + 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; + + /* 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; + bool particles_is_inactive(RID p_particles) const 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; + + // 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..1c946895a5 --- /dev/null +++ b/drivers/gles3/shader_gles3.cpp @@ -0,0 +1,699 @@ +/*************************************************************************/ +/* 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"; + + Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ); + if (f.is_null()) { + 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"; + + Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE); + ERR_FAIL_COND(f.is_null()); + 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()); + } +#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)) { + 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(); + + Ref<DirAccess> d = DirAccess::open(shader_cache_dir); + ERR_FAIL_COND(d.is_null()); + if (d->change_dir(name) != OK) { + Error err = d->make_dir(name); + ERR_FAIL_COND(err != OK); + d->change_dir(name); + } + + //erase other versions? + if (shader_cache_cleanup_on_start) { + } + // + if (d->change_dir(base_sha256) != OK) { + Error err = d->make_dir(base_sha256); + ERR_FAIL_COND(err != OK); + } + shader_cache_dir_valid = true; + + print_verbose("Shader '" + name + "' SHA256: " + base_sha256); + } + + 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/gles3/storage/SCsub b/drivers/gles3/storage/SCsub new file mode 100644 index 0000000000..91e1140b75 --- /dev/null +++ b/drivers/gles3/storage/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.drivers_sources, "*.cpp") 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/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp new file mode 100644 index 0000000000..e519c428d2 --- /dev/null +++ b/drivers/gles3/storage/material_storage.cpp @@ -0,0 +1,913 @@ +/*************************************************************************/ +/* material_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 "material_storage.h" +#include "config.h" +#include "texture_storage.h" + +#include "drivers/gles3/rasterizer_canvas_gles3.h" + +using namespace GLES3; + +MaterialStorage *MaterialStorage::singleton = nullptr; + +MaterialStorage *MaterialStorage::get_singleton() { + return singleton; +} + +MaterialStorage::MaterialStorage() { + singleton = this; + + 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); +} + +MaterialStorage::~MaterialStorage() { + shaders.copy.version_free(shaders.copy_version); + + singleton = nullptr; +} + +/* GLOBAL VARIABLE API */ + +void MaterialStorage::global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) { +} + +void MaterialStorage::global_variable_remove(const StringName &p_name) { +} + +Vector<StringName> MaterialStorage::global_variable_get_list() const { + return Vector<StringName>(); +} + +void MaterialStorage::global_variable_set(const StringName &p_name, const Variant &p_value) { +} + +void MaterialStorage::global_variable_set_override(const StringName &p_name, const Variant &p_value) { +} + +Variant MaterialStorage::global_variable_get(const StringName &p_name) const { + return Variant(); +} + +RS::GlobalVariableType MaterialStorage::global_variable_get_type(const StringName &p_name) const { + return RS::GLOBAL_VAR_TYPE_MAX; +} + +void MaterialStorage::global_variables_load_settings(bool p_load_textures) { +} + +void MaterialStorage::global_variables_clear() { +} + +int32_t MaterialStorage::global_variables_instance_allocate(RID p_instance) { + return 0; +} + +void MaterialStorage::global_variables_instance_free(RID p_instance) { +} + +void MaterialStorage::global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) { +} + +/* SHADER API */ + +void MaterialStorage::_shader_make_dirty(Shader *p_shader) { + if (p_shader->dirty_list.in_list()) { + return; + } + + _shader_dirty_list.add(&p_shader->dirty_list); +} + +RID MaterialStorage::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 MaterialStorage::shader_initialize(RID p_rid) { + // noop +} + +//RID MaterialStorage::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 MaterialStorage::shader_free(RID 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); +} + +void MaterialStorage::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 = &RasterizerCanvasGLES3::get_singleton()->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 MaterialStorage::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 MaterialStorage::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 MaterialStorage::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() && !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 MaterialStorage::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(); +} + +void MaterialStorage::_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 MaterialStorage::update_dirty_shaders() { + while (_shader_dirty_list.first()) { + _update_shader(_shader_dirty_list.first()->self()); + } +} + +/* MATERIAL API */ + +void MaterialStorage::_material_make_dirty(Material *p_material) const { + if (p_material->dirty_list.in_list()) { + return; + } + + _material_dirty_list.add(&p_material->dirty_list); +} + +void MaterialStorage::_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; + } + + { + if (p_material->shader && p_material->shader->mode == RS::SHADER_SPATIAL) { + bool can_cast_shadow = false; + bool is_animated = false; + + 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(); + } +} + +RID MaterialStorage::material_allocate() { + Material *material = memnew(Material); + return material_owner.make_rid(material); +} + +void MaterialStorage::material_initialize(RID p_rid) { +} + +//RID MaterialStorage::material_create() { +// Material *material = memnew(Material); + +// return material_owner.make_rid(material); +//} + +void MaterialStorage::material_free(RID 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); +} + +void MaterialStorage::material_set_shader(RID p_material, RID p_shader) { + Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + Shader *shader = get_shader(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); +} + +void MaterialStorage::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 MaterialStorage::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); +} + +void MaterialStorage::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; +} + +void MaterialStorage::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; +} + +bool MaterialStorage::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 MaterialStorage::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; +} + +Variant MaterialStorage::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 MaterialStorage::update_dirty_materials() { + while (_material_dirty_list.first()) { + Material *material = _material_dirty_list.first()->self(); + _update_material(material); + } +} + +/* are these still used? */ +RID MaterialStorage::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 MaterialStorage::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; +} + +bool MaterialStorage::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 MaterialStorage::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 MaterialStorage::material_add_instance_owner(RID p_material, RendererStorage::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 MaterialStorage::material_remove_instance_owner(RID p_material, RendererStorage::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 MaterialStorage::_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 MaterialStorage::_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); + } +} +*/ + +#endif // !GLES3_ENABLED diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h new file mode 100644 index 0000000000..450ee7191f --- /dev/null +++ b/drivers/gles3/storage/material_storage.h @@ -0,0 +1,338 @@ +/*************************************************************************/ +/* material_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 MATERIAL_STORAGE_GLES3_H +#define MATERIAL_STORAGE_GLES3_H + +#ifdef GLES3_ENABLED + +#include "core/templates/local_vector.h" +#include "core/templates/rid_owner.h" +#include "core/templates/self_list.h" +#include "servers/rendering/renderer_compositor.h" +#include "servers/rendering/renderer_storage.h" +#include "servers/rendering/shader_compiler.h" +#include "servers/rendering/shader_language.h" +#include "servers/rendering/storage/material_storage.h" + +#include "drivers/gles3/shaders/copy.glsl.gen.h" + +namespace GLES3 { + +/* SHADER Structs */ + +struct Shaders { + ShaderCompiler compiler; + + CopyShaderGLES3 copy; + RID copy_version; + //CubemapFilterShaderGLES3 cubemap_filter; + + ShaderCompiler::IdentifierActions actions_canvas; + ShaderCompiler::IdentifierActions actions_scene; + ShaderCompiler::IdentifierActions actions_particles; +}; + +struct Material; + +struct Shader { + RID self; + + RS::ShaderMode mode; + ShaderGLES3 *shader = nullptr; + 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; + } +}; + +/* MATERIAL Structs */ + +struct Material { + RID self; + Shader *shader = nullptr; + 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; + } +}; + +class MaterialStorage : public RendererMaterialStorage { +private: + static MaterialStorage *singleton; + + /* SHADER API */ + + mutable Shaders shaders; + + mutable RID_PtrOwner<Shader> shader_owner; + mutable SelfList<Shader>::List _shader_dirty_list; + + /* MATERIAL API */ + + mutable SelfList<Material>::List _material_dirty_list; + mutable RID_PtrOwner<Material> material_owner; + +public: + static MaterialStorage *get_singleton(); + + MaterialStorage(); + virtual ~MaterialStorage(); + + /* GLOBAL VARIABLE API */ + + virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) override; + virtual void global_variable_remove(const StringName &p_name) override; + virtual Vector<StringName> global_variable_get_list() const override; + + virtual void global_variable_set(const StringName &p_name, const Variant &p_value) override; + virtual void global_variable_set_override(const StringName &p_name, const Variant &p_value) override; + virtual Variant global_variable_get(const StringName &p_name) const override; + virtual RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const override; + + virtual void global_variables_load_settings(bool p_load_textures = true) override; + virtual void global_variables_clear() override; + + virtual int32_t global_variables_instance_allocate(RID p_instance) override; + virtual void global_variables_instance_free(RID p_instance) override; + virtual void global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) override; + + /* SHADER API */ + + Shader *get_shader(RID p_rid) { return shader_owner.get_or_null(p_rid); }; + bool owns_shader(RID p_rid) { return shader_owner.owns(p_rid); }; + + void _shader_make_dirty(Shader *p_shader); + + virtual RID shader_allocate() override; + virtual void shader_initialize(RID p_rid) override; + virtual void shader_free(RID p_rid) override; + + //RID shader_create() override; + + virtual void shader_set_code(RID p_shader, const String &p_code) override; + virtual String shader_get_code(RID p_shader) const override; + virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override; + + virtual void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture, int p_index) override; + virtual RID shader_get_default_texture_param(RID p_shader, const StringName &p_name, int p_index) const override; + + virtual 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(); } + + /* MATERIAL API */ + + Material *get_material(RID p_rid) { return material_owner.get_or_null(p_rid); }; + bool owns_material(RID p_rid) { return material_owner.owns(p_rid); }; + + void _material_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); + + // new + virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override {} + virtual void material_update_dependency(RID p_material, RendererStorage::DependencyTracker *p_instance) override {} + + // old + virtual RID material_allocate() override; + virtual void material_initialize(RID p_rid) override; + + virtual void material_free(RID p_rid) override; + + //RID material_create() override; + + virtual void material_set_shader(RID p_material, RID p_shader) override; + virtual RID material_get_shader(RID p_material) const; + + virtual void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) override; + virtual Variant material_get_param(RID p_material, const StringName &p_param) const override; + virtual Variant material_get_param_default(RID p_material, const StringName &p_param) const; + + void material_set_line_width(RID p_material, float p_width); + virtual void material_set_next_pass(RID p_material, RID p_next_material) override; + + virtual bool material_is_animated(RID p_material) override; + virtual 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, RendererStorage::DependencyTracker *p_instance); + void material_remove_instance_owner(RID p_material, RendererStorage::DependencyTracker *p_instance); + + void material_set_render_priority(RID p_material, int priority) override; + + void update_dirty_materials(); +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // !MATERIAL_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp new file mode 100644 index 0000000000..c2a431aff1 --- /dev/null +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -0,0 +1,257 @@ +/*************************************************************************/ +/* mesh_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 "mesh_storage.h" + +using namespace GLES3; + +MeshStorage *MeshStorage::singleton = nullptr; + +MeshStorage *MeshStorage::get_singleton() { + return singleton; +} + +MeshStorage::MeshStorage() { + singleton = this; +} + +MeshStorage::~MeshStorage() { + singleton = nullptr; +} + +/* MESH API */ + +RID MeshStorage::mesh_allocate() { + return RID(); +} + +void MeshStorage::mesh_initialize(RID p_rid) { +} + +void MeshStorage::mesh_free(RID p_rid) { +} + +void MeshStorage::mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) { +} + +bool MeshStorage::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) { + return false; +} + +void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) { +} + +int MeshStorage::mesh_get_blend_shape_count(RID p_mesh) const { + return 0; +} + +void MeshStorage::mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) { +} + +RS::BlendShapeMode MeshStorage::mesh_get_blend_shape_mode(RID p_mesh) const { + return RS::BLEND_SHAPE_MODE_NORMALIZED; +} + +void MeshStorage::mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +} + +void MeshStorage::mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +} + +void MeshStorage::mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +} + +void MeshStorage::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { +} + +RID MeshStorage::mesh_surface_get_material(RID p_mesh, int p_surface) const { + return RID(); +} + +RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { + return RS::SurfaceData(); +} + +int MeshStorage::mesh_get_surface_count(RID p_mesh) const { + return 1; +} + +void MeshStorage::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { +} + +AABB MeshStorage::mesh_get_custom_aabb(RID p_mesh) const { + return AABB(); +} + +AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { + return AABB(); +} + +void MeshStorage::mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) { +} + +void MeshStorage::mesh_clear(RID p_mesh) { +} + +/* MESH INSTANCE API */ + +RID MeshStorage::mesh_instance_create(RID p_base) { + return RID(); +} + +void MeshStorage::mesh_instance_free(RID p_rid) { +} + +void MeshStorage::mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) { +} + +void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) { +} + +void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { +} + +void MeshStorage::update_mesh_instances() { +} + +/* MULTIMESH API */ + +RID MeshStorage::multimesh_allocate() { + return RID(); +} + +void MeshStorage::multimesh_initialize(RID p_rid) { +} + +void MeshStorage::multimesh_free(RID p_rid) { +} + +void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { +} + +int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const { + return 0; +} + +void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { +} + +void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { +} + +void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { +} + +void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { +} + +void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { +} + +RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const { + return RID(); +} + +AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { + return AABB(); +} + +Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { + return Transform3D(); +} + +Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { + return Transform2D(); +} + +Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const { + return Color(); +} + +Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { + return Color(); +} + +void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { +} + +Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { + return Vector<float>(); +} + +void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { +} + +int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const { + return 0; +} + +/* SKELETON API */ + +RID MeshStorage::skeleton_allocate() { + return RID(); +} + +void MeshStorage::skeleton_initialize(RID p_rid) { +} + +void MeshStorage::skeleton_free(RID p_rid) { +} + +void MeshStorage::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton) { +} + +void MeshStorage::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { +} + +int MeshStorage::skeleton_get_bone_count(RID p_skeleton) const { + return 0; +} + +void MeshStorage::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) { +} + +Transform3D MeshStorage::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { + return Transform3D(); +} + +void MeshStorage::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { +} + +Transform2D MeshStorage::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { + return Transform2D(); +} + +void MeshStorage::skeleton_update_dependency(RID p_base, RendererStorage::DependencyTracker *p_instance) { +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h new file mode 100644 index 0000000000..3f44908049 --- /dev/null +++ b/drivers/gles3/storage/mesh_storage.h @@ -0,0 +1,204 @@ +/*************************************************************************/ +/* mesh_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 MESH_STORAGE_GLES3_H +#define MESH_STORAGE_GLES3_H + +#ifdef GLES3_ENABLED + +#include "core/templates/local_vector.h" +#include "core/templates/rid_owner.h" +#include "core/templates/self_list.h" +#include "servers/rendering/storage/mesh_storage.h" + +namespace GLES3 { + +class MeshStorage : public RendererMeshStorage { +private: + static MeshStorage *singleton; + +public: + static MeshStorage *get_singleton(); + + MeshStorage(); + virtual ~MeshStorage(); + + /* MESH API */ + + virtual RID mesh_allocate() override; + virtual void mesh_initialize(RID p_rid) override; + virtual void mesh_free(RID p_rid) override; + + virtual void mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) override; + virtual bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override; + + virtual void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) override; + + virtual int mesh_get_blend_shape_count(RID p_mesh) const override; + + virtual void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) override; + virtual RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const override; + + virtual void mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; + virtual void mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; + virtual void mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; + + virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override; + virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const override; + + virtual RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override; + virtual int mesh_get_surface_count(RID p_mesh) const override; + + virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override; + virtual AABB mesh_get_custom_aabb(RID p_mesh) const override; + + virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override; + virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override; + virtual void mesh_clear(RID p_mesh) override; + + /* MESH INSTANCE API */ + + virtual RID mesh_instance_create(RID p_base) override; + virtual void mesh_instance_free(RID p_rid) override; + virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override; + virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override; + virtual void mesh_instance_check_for_update(RID p_mesh_instance) override; + virtual void update_mesh_instances() 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; + + RendererStorage::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(); + + virtual RID multimesh_allocate() override; + virtual void multimesh_initialize(RID p_rid) override; + virtual void multimesh_free(RID p_rid) override; + virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; + virtual int multimesh_get_instance_count(RID p_multimesh) const override; + + virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; + virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; + virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; + virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; + virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; + + virtual RID multimesh_get_mesh(RID p_multimesh) const override; + virtual AABB multimesh_get_aabb(RID p_multimesh) const override; + + virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; + virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; + virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override; + virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; + virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; + virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override; + + virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; + virtual int multimesh_get_visible_instances(RID p_multimesh) const override; + + _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 */ + + virtual RID skeleton_allocate() override; + virtual void skeleton_initialize(RID p_rid) override; + virtual void skeleton_free(RID p_rid) override; + + virtual void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override; + virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override; + virtual int skeleton_get_bone_count(RID p_skeleton) const override; + virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) override; + virtual Transform3D skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override; + virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) override; + virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const override; + + virtual void skeleton_update_dependency(RID p_base, RendererStorage::DependencyTracker *p_instance) override; +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // !MESH_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..7656cdf67e --- /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 = nullptr; + 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 = nullptr; + + Vector<Ref<Image>> images; + + bool redraw_if_visible; + + RS::TextureDetectCallback detect_3d; + void *detect_3d_ud = nullptr; + + RS::TextureDetectCallback detect_srgb; + void *detect_srgb_ud = nullptr; + + RS::TextureDetectCallback detect_normal; + void *detect_normal_ud = nullptr; + + 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/gles3/texture_loader_gles3.cpp b/drivers/gles3/texture_loader_gles3.cpp new file mode 100644 index 0000000000..8c8724686d --- /dev/null +++ b/drivers/gles3/texture_loader_gles3.cpp @@ -0,0 +1,112 @@ +/*************************************************************************/ +/* texture_loader_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 "texture_loader_gles3.h" + +#ifdef GLES3_ENABLED + +#include "core/io/file_access.h" +#include "core/string/print_string.h" + +#include <string.h> + +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; + + //We just use some format + Image::Format fmt = Image::FORMAT_RGB8; + int rowsize = 3 * width; + + Vector<uint8_t> dstbuff; + + dstbuff.resize(rowsize * height); + + uint8_t **row_p = memnew_arr(uint8_t *, height); + + for (unsigned int i = 0; i < height; i++) { + row_p[i] = nullptr; // No colors any more, I want them to turn black. + } + + memdelete_arr(row_p); + + Ref<Image> img = memnew(Image(width, height, 0, fmt, dstbuff)); + + Ref<ImageTexture> texture = memnew(ImageTexture); + texture->create_from_image(img); + + if (r_error) { + *r_error = OK; + } + + return texture; +} + +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"); + p_extensions->push_back("jpeg"); + p_extensions->push_back("jpg"); + p_extensions->push_back("hdr"); + p_extensions->push_back("pkm"); + p_extensions->push_back("png"); + p_extensions->push_back("pvr"); + p_extensions->push_back("svg"); + p_extensions->push_back("tga"); + p_extensions->push_back("webp"); +} + +bool ResourceFormatGLES2Texture::handles_type(const String &p_type) const { + return ClassDB::is_parent_class(p_type, "Texture2D"); +} + +String ResourceFormatGLES2Texture::get_resource_type(const String &p_path) const { + String extension = p_path.get_extension().to_lower(); + if ( + extension == "bmp" || + extension == "dds" || + extension == "exr" || + extension == "jpeg" || + extension == "jpg" || + extension == "hdr" || + extension == "pkm" || + extension == "png" || + extension == "pvr" || + extension == "svg" || + extension == "tga" || + extension == "webp") { + return "ImageTexture"; + } + + return ""; +} + +#endif diff --git a/drivers/gles3/texture_loader_gles3.h b/drivers/gles3/texture_loader_gles3.h new file mode 100644 index 0000000000..54ddf80a96 --- /dev/null +++ b/drivers/gles3/texture_loader_gles3.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* texture_loader_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 TEXTURE_LOADER_OPENGL_H +#define TEXTURE_LOADER_OPENGL_H + +#ifdef GLES3_ENABLED + +#include "core/io/resource_loader.h" +#include "scene/resources/texture.h" + +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 ~ResourceFormatGLES2Texture() {} +}; + +#endif // GLES3_ENABLED + +#endif // TEXTURE_LOADER_OPENGL_H diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index f7f7f2dd22..917bfec574 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 */ @@ -36,18 +36,16 @@ #include <string.h> -Error ImageLoaderPNG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) { +Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) { const uint64_t buffer_size = f->get_length(); Vector<uint8_t> file_buffer; Error err = file_buffer.resize(buffer_size); if (err) { - f->close(); return err; } { uint8_t *writer = file_buffer.ptrw(); f->get_buffer(writer, buffer_size); - f->close(); } const uint8_t *reader = file_buffer.ptr(); return PNGDriverCommon::png_to_image(reader, buffer_size, p_force_linear, p_image); diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h index b4a58616f6..522cc901d4 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 */ @@ -40,7 +40,7 @@ private: static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size); public: - virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderPNG(); }; diff --git a/drivers/png/png_driver_common.cpp b/drivers/png/png_driver_common.cpp index 412e17c6b7..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 */ 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 2971880985..8633d2dc4e 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 */ @@ -46,26 +46,22 @@ Error ResourceSaverPNG::save(const String &p_path, const RES &p_resource, uint32 Error err = save_image(p_path, img); return err; -}; +} Error ResourceSaverPNG::save_image(const String &p_path, const Ref<Image> &p_img) { Vector<uint8_t> buffer; Error err = PNGDriverCommon::image_to_png(p_img, buffer); ERR_FAIL_COND_V_MSG(err, err, "Can't convert image to PNG."); - FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save PNG at path: '%s'.", p_path)); const uint8_t *reader = buffer.ptr(); file->store_buffer(reader, buffer.size()); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { - memdelete(file); return ERR_CANT_CREATE; } - file->close(); - memdelete(file); - return OK; } @@ -89,4 +85,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 5e2431d44e..0c27b76ed8 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,13 +34,14 @@ #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; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); switch (pa_context_get_state(c)) { case PA_CONTEXT_TERMINATED: @@ -64,7 +65,7 @@ void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { } void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); // If eol is set to a positive number, you're at the end of the list if (eol > 0) { @@ -83,7 +84,7 @@ void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l } void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); // If eol is set to a positive number, you're at the end of the list if (eol > 0) { @@ -103,7 +104,7 @@ void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_inf void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) { ERR_FAIL_COND_MSG(!i, "PulseAudio server info is null."); - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); ad->capture_default_device = i->default_source_name; ad->default_device = i->default_sink_name; @@ -191,7 +192,7 @@ Error AudioDriverPulseAudio::init_device() { 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 channels"); + ERR_PRINT("PulseAudio: init device failed to detect number of output channels"); return err; } @@ -211,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; @@ -221,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; @@ -293,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; @@ -371,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; @@ -382,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 @@ -399,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; } } } @@ -463,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; @@ -577,7 +588,7 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const { } void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); // If eol is set to a positive number, you're at the end of the list if (eol > 0) { @@ -689,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; @@ -758,7 +771,7 @@ void AudioDriverPulseAudio::capture_set_device(const String &p_name) { } void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); // If eol is set to a positive number, you're at the end of the list if (eol > 0) { diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index 1358561c02..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 */ diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp index 4a163b7c10..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 */ 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 1754b47c85..7e6105f033 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 */ @@ -49,7 +49,7 @@ #include <mntent.h> #endif -DirAccess *DirAccessUnix::create_fs() { +Ref<DirAccess> DirAccessUnix::create_fs() { return memnew(DirAccessUnix); } @@ -138,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) { @@ -216,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"); @@ -286,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; } @@ -304,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; } @@ -342,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); @@ -358,9 +374,9 @@ Error DirAccessUnix::change_dir(String p_dir) { return OK; } -String DirAccessUnix::get_current_dir(bool p_include_drive) { +String DirAccessUnix::get_current_dir(bool p_include_drive) const { 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()); @@ -458,14 +474,14 @@ uint64_t DirAccessUnix::get_space_left() { struct statvfs vfs; if (statvfs(current_dir.utf8().get_data(), &vfs) != 0) { return 0; - }; + } 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 8b19308967..4fea7cd154 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 */ @@ -41,9 +41,9 @@ #include <unistd.h> class DirAccessUnix : public DirAccess { - DIR *dir_stream; + DIR *dir_stream = nullptr; - static DirAccess *create_fs(); + static Ref<DirAccess> create_fs(); String current_dir; bool _cisdir; @@ -63,10 +63,11 @@ 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 - virtual String get_current_dir(bool p_include_drive = true); ///< return current dir location + virtual String get_current_dir(bool p_include_drive = true) const; ///< return current dir location virtual Error make_dir(String p_dir); virtual bool file_exists(String p_file); diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index f07c654bd6..99da292e12 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 */ @@ -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) { @@ -246,7 +246,7 @@ uint64_t FileAccessUnix::get_buffer(uint8_t *p_dst, uint64_t p_length) const { uint64_t read = fread(p_dst, 1, p_length, f); check_errors(); return read; -}; +} Error FileAccessUnix::get_error() const { return last_error; @@ -285,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 @@ -307,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) { @@ -320,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) { @@ -334,7 +336,7 @@ Error FileAccessUnix::_set_unix_permissions(const String &p_file, uint32_t p_per return FAILED; } -FileAccess *FileAccessUnix::create_libc() { +Ref<FileAccess> FileAccessUnix::create_libc() { return memnew(FileAccessUnix); } diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 8b27c308e1..692776915c 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 */ @@ -49,7 +49,7 @@ class FileAccessUnix : public FileAccess { String path; String path_src; - static FileAccess *create_libc(); + static Ref<FileAccess> create_libc(); public: static CloseNotificationFunc close_notification_func; diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index 42b5bbfaf2..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 */ @@ -72,10 +72,10 @@ static IPAddress _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; -}; +} void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const { struct addrinfo hints; @@ -90,14 +90,14 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos } 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; - }; + } if (result == nullptr || result->ai_addr == nullptr) { ERR_PRINT("Invalid response from getaddrinfo"); @@ -105,7 +105,7 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos freeaddrinfo(result); } return; - }; + } struct addrinfo *next = result; @@ -115,7 +115,7 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos continue; } IPAddress ip = _sockaddr2ip(next->ai_addr); - if (!r_addresses.find(ip)) { + if (ip.is_valid() && !r_addresses.find(ip)) { r_addresses.push_back(ip); } next = next->ai_next; @@ -138,8 +138,9 @@ void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) con 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); @@ -171,14 +172,14 @@ void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) con 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; @@ -191,19 +192,21 @@ void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) con 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 diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index 0d64648b39..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 */ diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index 768b819650..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 */ @@ -147,7 +147,7 @@ void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ if (r_port) { *r_port = ntohs(addr6->sin6_port); } - }; + } } NetSocket *NetSocketPosix::_create_func() { @@ -325,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; @@ -420,7 +421,7 @@ Error NetSocketPosix::listen(int p_max_pending) { print_verbose("Failed to listen from socket."); close(); return FAILED; - }; + } return OK; } @@ -494,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(); @@ -503,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 diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h index 38c8170a52..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 */ diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 193825a73f..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()) { @@ -150,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"; } @@ -158,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, <); @@ -181,10 +221,10 @@ 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, <); @@ -249,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. @@ -318,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. @@ -370,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; @@ -399,12 +439,12 @@ Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle } 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()); } @@ -460,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); @@ -473,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 { @@ -486,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(); } @@ -515,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; diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index eb36bf4255..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; @@ -54,15 +53,7 @@ public: 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; @@ -72,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; @@ -81,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; @@ -102,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, bool p_editor_notify, 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 e47046e3ae..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 */ 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 8fe75367a8..b6ceb1cdea 100644 --- a/drivers/vulkan/SCsub +++ b/drivers/vulkan/SCsub @@ -40,6 +40,9 @@ 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) diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index e7ac447426..400802686e 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 */ @@ -43,6 +43,8 @@ //#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; @@ -387,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] = { @@ -616,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) { @@ -970,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"); } @@ -1048,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; @@ -1138,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: { } } @@ -1167,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: { } } @@ -1231,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; @@ -1239,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; } @@ -1373,7 +1318,7 @@ const VkImageType RenderingDeviceVulkan::vulkan_image_type[RenderingDevice::TEXT /**** BUFFER MANAGEMENT ****/ /***************************/ -Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mapping) { +Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mem_usage, VmaAllocationCreateFlags p_mem_flags) { VkBufferCreateInfo bufferInfo; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.pNext = nullptr; @@ -1385,13 +1330,18 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, bufferInfo.pQueueFamilyIndices = nullptr; VmaAllocationCreateInfo allocInfo; - allocInfo.flags = 0; - allocInfo.usage = p_mapping; + allocInfo.flags = p_mem_flags; + allocInfo.usage = p_mem_usage; allocInfo.requiredFlags = 0; allocInfo.preferredFlags = 0; 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) + "."); @@ -1430,8 +1380,8 @@ Error RenderingDeviceVulkan::_insert_staging_block() { bufferInfo.pQueueFamilyIndices = nullptr; VmaAllocationCreateInfo allocInfo; - allocInfo.flags = 0; - allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; allocInfo.requiredFlags = 0; allocInfo.preferredFlags = 0; allocInfo.memoryTypeBits = 0; @@ -1534,7 +1484,7 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_ //this is an old block, which was already processed, let's reuse staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; staging_buffer_blocks.write[staging_buffer_current].fill_amount = 0; - } else if (staging_buffer_blocks[staging_buffer_current].frame_used > frames_drawn - frame_count) { + } else { //this block may still be in use, let's not touch it unless we have to, so.. can we create a new one? 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 @@ -1893,14 +1843,22 @@ 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.usage = p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT ? VMA_MEMORY_USAGE_CPU_ONLY : VMA_MEMORY_USAGE_GPU_ONLY; + allocInfo.flags = (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT : 0; + allocInfo.pool = nullptr; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; 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; @@ -2170,7 +2128,125 @@ 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.get_or_null(p_with_texture); @@ -2194,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; @@ -2206,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; @@ -2269,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; @@ -2479,8 +2556,8 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co } 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 @@ -2520,7 +2597,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co 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) { @@ -2626,7 +2703,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t //allocate buffer VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; //makes more sense to retrieve Buffer tmp_buffer; - _buffer_allocate(&tmp_buffer, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + _buffer_allocate(&tmp_buffer, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT); { //Source image barrier VkImageMemoryBarrier image_memory_barrier; @@ -2678,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; } @@ -3273,10 +3350,10 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f 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 }, @@ -3287,8 +3364,14 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF attachment_last_pass.resize(p_attachments.size()); Vector<VkAttachmentDescription> attachments; + Vector<int> attachment_remap; for (int i = 0; i < p_attachments.size(); i++) { + if (p_attachments[i].usage_flags == AttachmentFormat::UNUSED_ATTACHMENT) { + attachment_remap.push_back(VK_ATTACHMENT_UNUSED); + continue; + } + 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)), @@ -3490,7 +3573,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } attachment_last_pass[i] = -1; - + attachment_remap.push_back(attachments.size()); attachments.push_back(description); } @@ -3535,7 +3618,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } 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.attachment = attachment_remap[attachment]; reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachment_last_pass[attachment] = i; } @@ -3554,7 +3637,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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.attachment = attachment_remap[attachment]; reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachment_last_pass[attachment] = i; } @@ -3580,7 +3663,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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.attachment = attachment_remap[attachment]; reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachment_last_pass[attachment] = i; } @@ -3594,7 +3677,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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.attachment = attachment_remap[attachment]; depth_stencil_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachment_last_pass[attachment] = i; @@ -3730,7 +3813,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF // 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; @@ -3881,14 +3964,13 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac 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"); + ERR_FAIL_COND_V_MSG(texture && 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) { + if (texture && texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { pass.depth_attachment = i; } else { - pass.color_attachments.push_back(i); + pass.color_attachments.push_back(texture ? i : FramebufferPass::ATTACHMENT_UNUSED); } } @@ -3902,29 +3984,35 @@ RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_tex _THREAD_SAFE_METHOD_ Vector<AttachmentFormat> attachments; + attachments.resize(p_texture_attachments.size()); Size2i size; - + bool size_set = false; for (int i = 0; i < p_texture_attachments.size(); i++) { + AttachmentFormat af; 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."); + if (!texture) { + af.usage_flags = AttachmentFormat::UNUSED_ATTACHMENT; + } else { + ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); - ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + if (!size_set) { + size.width = texture->width; + size.height = texture->height; + size_set = true; + } else { + ERR_FAIL_COND_V_MSG((uint32_t)size.width != texture->width || (uint32_t)size.height != texture->height, RID(), + "All textures in a framebuffer should be the same size."); + } - if (i == 0) { - size.width = texture->width; - size.height = texture->height; - } else { - ERR_FAIL_COND_V_MSG((uint32_t)size.width != texture->width || (uint32_t)size.height != texture->height, RID(), - "All textures in a framebuffer should be the same size."); + af.format = texture->format; + af.samples = texture->samples; + af.usage_flags = texture->usage_flags; } - - AttachmentFormat af; - af.format = texture->format; - af.samples = texture->samples; - af.usage_flags = texture->usage_flags; - attachments.push_back(af); + attachments.write[i] = af; } + ERR_FAIL_COND_V_MSG(!size_set, RID(), "All attachments unused."); + FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count); if (format_id == INVALID_ID) { return RID(); @@ -3942,7 +4030,9 @@ RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_tex RID id = framebuffer_owner.make_rid(framebuffer); for (int i = 0; i < p_texture_attachments.size(); i++) { - _add_dependency(id, p_texture_attachments[i]); + if (p_texture_attachments[i].is_valid()) { + _add_dependency(id, p_texture_attachments[i]); + } } return id; @@ -4020,7 +4110,7 @@ RID RenderingDeviceVulkan::vertex_buffer_create(uint32_t p_size_bytes, const Vec usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; } Buffer buffer; - _buffer_allocate(&buffer, p_size_bytes, usage, VMA_MEMORY_USAGE_GPU_ONLY); + _buffer_allocate(&buffer, p_size_bytes, usage, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); if (p_data.size()) { uint64_t data_size = p_data.size(); const uint8_t *r = p_data.ptr(); @@ -4182,7 +4272,7 @@ RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBuff #else index_buffer.max_index = 0xFFFFFFFF; #endif - _buffer_allocate(&index_buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + _buffer_allocate(&index_buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); if (p_data.size()) { uint64_t data_size = p_data.size(); const uint8_t *r = p_data.ptr(); @@ -4249,7 +4339,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); @@ -4686,19 +4776,22 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve for (uint32_t j = 0; j < sc_count; j++) { int32_t existing = -1; RenderingDeviceVulkanShaderBinarySpecializationConstant sconst; - sconst.constant_id = spec_constants[j]->constant_id; - switch (spec_constants[j]->constant_type) { + 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 = spec_constants[j]->default_value.int_bool_value != 0; + 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 = spec_constants[j]->default_value.int_bool_value; + 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 = spec_constants[j]->default_value.float_value; + sconst.float_value = spc->default_value.float_value; } break; } sconst.stage_flags = 1 << p_spirv[i].shader_stage; @@ -4782,9 +4875,8 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve "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); + Ref<FileAccess> f = FileAccess::open("res://popo.spv", FileAccess::WRITE); f->store_buffer((const uint8_t *)&SpirV[0], SpirV.size() * sizeof(uint32_t)); - memdelete(f); } #endif @@ -4872,8 +4964,8 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve Vector<uint8_t> ret; ret.resize(total_size); - uint32_t offset = 0; { + uint32_t offset = 0; uint8_t *binptr = ret.ptrw(); binptr[0] = 'G'; binptr[1] = 'V'; @@ -4946,7 +5038,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ uint32_t bin_data_size = decode_uint32(binptr + 8); - const RenderingDeviceVulkanShaderBinaryData &binary_data = *(const RenderingDeviceVulkanShaderBinaryData *)(binptr + 12); + const RenderingDeviceVulkanShaderBinaryData &binary_data = *(reinterpret_cast<const RenderingDeviceVulkanShaderBinaryData *>(binptr + 12)); Shader::PushConstant push_constant; push_constant.push_constant_size = binary_data.push_constant_size; @@ -4958,7 +5050,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ 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] }; + const 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; @@ -4982,7 +5074,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ 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); + const RenderingDeviceVulkanShaderBinaryDataBinding *set_ptr = reinterpret_cast<const RenderingDeviceVulkanShaderBinaryDataBinding *>(binptr + read_offset); uint32_t set_size = set_count * sizeof(RenderingDeviceVulkanShaderBinaryDataBinding); ERR_FAIL_COND_V(read_offset + set_size >= binsize, RID()); @@ -5054,7 +5146,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ 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); + const RenderingDeviceVulkanShaderBinarySpecializationConstant &src_sc = *(reinterpret_cast<const RenderingDeviceVulkanShaderBinarySpecializationConstant *>(binptr + read_offset)); Shader::SpecializationConstant sc; sc.constant.int_value = src_sc.int_value; sc.constant.type = PipelineSpecializationConstantType(src_sc.type); @@ -5291,7 +5383,7 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve "Creating buffers with data is forbidden during creation of a draw list"); Buffer buffer; - Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); ERR_FAIL_COND_V(err != OK, RID()); if (p_data.size()) { uint64_t data_size = p_data.size(); @@ -5317,7 +5409,7 @@ RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Ve if (p_usage & STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT) { flags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; } - Error err = _buffer_allocate(&buffer, p_size_bytes, flags, VMA_MEMORY_USAGE_GPU_ONLY); + Error err = _buffer_allocate(&buffer, p_size_bytes, flags, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); ERR_FAIL_COND_V(err != OK, RID()); if (p_data.size()) { @@ -5343,7 +5435,7 @@ RID RenderingDeviceVulkan::texture_buffer_create(uint32_t p_size_elements, DataF ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != size_bytes, RID()); TextureBuffer texture_buffer; - Error err = _buffer_allocate(&texture_buffer.buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + Error err = _buffer_allocate(&texture_buffer.buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); ERR_FAIL_COND_V(err != OK, RID()); if (p_data.size()) { @@ -5552,18 +5644,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.get_or_null(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; @@ -5575,31 +5667,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.get_or_null(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.get_or_null(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(), @@ -5612,7 +5704,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, 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); } @@ -5631,28 +5723,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.get_or_null(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(), @@ -5665,7 +5757,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, 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); } @@ -5685,27 +5777,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.get_or_null(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."); @@ -5733,29 +5825,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.get_or_null(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); @@ -5763,21 +5855,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()) + ")."); } } @@ -5785,11 +5877,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.get_or_null(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.get_or_null(uniform.ids[j + 1]); + TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j + 1)); VkDescriptorImageInfo img_info; img_info.sampler = *sampler; @@ -5805,23 +5897,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.get_or_null(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(), @@ -5836,15 +5928,15 @@ 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.get_or_null(uniform.ids[0]); - } else if (vertex_buffer_owner.owns(uniform.ids[0])) { - buffer = vertex_buffer_owner.get_or_null(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."); } @@ -5864,18 +5956,18 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, 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.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(), "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.ids.size()) + ")."); + 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.ids.size()) + ")."); + 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 (int j = 0; j < uniform.ids.size(); j++) { - Texture *texture = texture_owner.get_or_null(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(), "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); @@ -5898,13 +5990,13 @@ 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_INPUT_ATTACHMENT; 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; default: { } @@ -5954,10 +6046,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)); } } @@ -6091,7 +6182,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { VkCommandBuffer command_buffer = frames[frame].setup_command_buffer; Buffer tmp_buffer; - _buffer_allocate(&tmp_buffer, buffer->size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + _buffer_allocate(&tmp_buffer, buffer->size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT); VkBufferCopy region; region.srcOffset = 0; region.dstOffset = 0; @@ -6242,7 +6333,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma rasterization_state_create_info.depthClampEnable = p_rasterization_state.enable_depth_clamp; rasterization_state_create_info.rasterizerDiscardEnable = p_rasterization_state.discard_primitives; rasterization_state_create_info.polygonMode = (p_rasterization_state.wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL); - static VkCullModeFlags cull_mode[3] = { + static const VkCullModeFlags cull_mode[3] = { VK_CULL_MODE_NONE, VK_CULL_MODE_FRONT_BIT, VK_CULL_MODE_BACK_BIT @@ -6269,7 +6360,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma Vector<VkSampleMask> sample_mask; if (p_multisample_state.sample_mask.size()) { //use sample mask - int rasterization_sample_mask_expected_size[TEXTURE_SAMPLES_MAX] = { + const int rasterization_sample_mask_expected_size[TEXTURE_SAMPLES_MAX] = { 1, 2, 4, 8, 16, 32, 64 }; ERR_FAIL_COND_V(rasterization_sample_mask_expected_size[p_multisample_state.sample_count] != p_multisample_state.sample_mask.size(), RID()); @@ -6338,49 +6429,55 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma Vector<VkPipelineColorBlendAttachmentState> attachment_states; { const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; - + attachment_states.resize(pass.color_attachments.size()); + ERR_FAIL_COND_V(p_blend_state.attachments.size() < pass.color_attachments.size(), RID()); 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]; + VkPipelineColorBlendAttachmentState state; + if (pass.color_attachments[i] == FramebufferPass::ATTACHMENT_UNUSED) { + state.blendEnable = false; + + state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO; + state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + state.colorBlendOp = VK_BLEND_OP_ADD; + + state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + state.alphaBlendOp = VK_BLEND_OP_ADD; + + state.colorWriteMask = 0; + } else { + 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[idx].write_r) { + if (p_blend_state.attachments[i].write_r) { state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; } - if (p_blend_state.attachments[idx].write_g) { + if (p_blend_state.attachments[i].write_g) { state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; } - if (p_blend_state.attachments[idx].write_b) { + if (p_blend_state.attachments[i].write_b) { state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; } - if (p_blend_state.attachments[idx].write_a) { + if (p_blend_state.attachments[i].write_a) { state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; } - - attachment_states.push_back(state); - idx++; - }; + } + attachment_states.write[i] = state; } - - ERR_FAIL_COND_V(attachment_states.size() != p_blend_state.attachments.size(), RID()); } color_blend_state_create_info.attachmentCount = attachment_states.size(); @@ -6703,6 +6800,10 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di VkCommandBuffer command_buffer = frames[frame].draw_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); @@ -6780,10 +6881,11 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu Vector<VkImageView> attachments; for (int i = 0; i < p_framebuffer->texture_ids.size(); 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); - ERR_FAIL_COND_V(texture->height != p_framebuffer->size.height, ERR_BUG); + if (texture) { + attachments.push_back(texture->view); + ERR_FAIL_COND_V(texture->width != p_framebuffer->size.width, ERR_BUG); + ERR_FAIL_COND_V(texture->height != p_framebuffer->size.height, ERR_BUG); + } } framebuffer_create_info.attachmentCount = attachments.size(); framebuffer_create_info.pAttachments = attachments.ptr(); @@ -6827,13 +6929,18 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff Vector<VkClearValue> clear_values; clear_values.resize(framebuffer->texture_ids.size()); - + int clear_values_count = 0; { int color_index = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); VkClearValue clear_value; + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + if (!texture) { + color_index++; + continue; + } + if (color_index < p_clear_colors.size() && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); //a bug Color clear_color = p_clear_colors[color_index]; @@ -6851,15 +6958,18 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff clear_value.color.float32[2] = 0; clear_value.color.float32[3] = 0; } - clear_values.write[i] = clear_value; + clear_values.write[clear_values_count++] = clear_value; } } - render_pass_begin.clearValueCount = clear_values.size(); + render_pass_begin.clearValueCount = clear_values_count; render_pass_begin.pClearValues = clear_values.ptr(); for (int i = 0; i < p_storage_textures.size(); i++) { Texture *texture = texture_owner.get_or_null(p_storage_textures[i]); + if (!texture) { + continue; + } 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) { @@ -6898,6 +7008,9 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff for (int i = 0; i < framebuffer->texture_ids.size(); i++) { Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + if (!texture) { + continue; + } texture->bound = true; draw_list_bound_textures.push_back(framebuffer->texture_ids[i]); } @@ -6906,15 +7019,21 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff } void RenderingDeviceVulkan::_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) { + ERR_FAIL_COND_MSG(p_clear_color && p_clear_colors.size() != framebuffer->texture_ids.size(), "Clear color values supplied (" + itos(p_clear_colors.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(framebuffer->texture_ids.size()) + ")."); Vector<VkClearAttachment> clear_attachments; int color_index = 0; + int texture_index = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - VkClearAttachment clear_at = {}; + if (!texture) { + texture_index++; + continue; + } + + VkClearAttachment clear_at = {}; if (p_clear_color && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - ERR_FAIL_INDEX(color_index, p_clear_colors.size()); //a bug - Color clear_color = p_clear_colors[color_index]; + Color clear_color = p_clear_colors[texture_index++]; clear_at.clearValue.color.float32[0] = clear_color.r; clear_at.clearValue.color.float32[1] = clear_color.g; clear_at.clearValue.color.float32[2] = clear_color.b; @@ -6990,18 +7109,14 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu } if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values - 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) { + if (!texture || !(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_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) + ")."); + 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; @@ -7093,7 +7208,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p 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) { + if (!texture || !(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { color_count++; } } @@ -7574,6 +7689,9 @@ Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_spli } 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; @@ -7684,6 +7802,9 @@ void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) { 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) { @@ -7797,6 +7918,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; @@ -8049,12 +8173,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 @@ -8103,6 +8230,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 @@ -8261,6 +8394,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) { @@ -8419,11 +8555,11 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) { } else if (uniform_set_owner.owns(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(p_id, uniform_set->invalidated_callback_userdata); + uniform_set->invalidated_callback(uniform_set->invalidated_callback_userdata); } - - uniform_set_owner.free(p_id); } else if (render_pipeline_owner.owns(p_id)) { RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id); frames[frame].render_pipelines_to_dispose_of.push_back(*pipeline); @@ -8516,6 +8652,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(); } @@ -8617,6 +8758,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 @@ -8737,9 +8902,9 @@ uint64_t RenderingDeviceVulkan::get_memory_usage(MemoryType p_type) const { } else if (p_type == MEMORY_TEXTURES) { return image_memory; } else { - VmaStats stats; - vmaCalculateStats(allocator, &stats); - return stats.total.usedBytes; + VmaTotalStatistics stats; + vmaCalculateStatistics(allocator, &stats); + return stats.total.statistics.allocationBytes; } } @@ -8813,6 +8978,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de // 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; @@ -8916,10 +9082,10 @@ 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) { @@ -8970,7 +9136,7 @@ void RenderingDeviceVulkan::_free_rids(T &p_owner, const char *p_type) { } 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 timestap was: " + 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 @@ -8980,35 +9146,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); } @@ -9025,49 +9191,49 @@ uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, R switch (p_resource) { case DRIVER_RESOURCE_VULKAN_DEVICE: { return (uint64_t)context->get_device(); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE: { return (uint64_t)context->get_physical_device(); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_INSTANCE: { return (uint64_t)context->get_instance(); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_QUEUE: { return (uint64_t)context->get_graphics_queue(); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX: { return context->get_graphics_queue_family_index(); - }; break; + } 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; + } 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; + } 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; + } 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; + } 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; + } break; case DRIVER_RESOURCE_VULKAN_BUFFER: { Buffer *buffer = nullptr; if (vertex_buffer_owner.owns(p_rid)) { @@ -9085,23 +9251,23 @@ uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, R ERR_FAIL_NULL_V(buffer, 0); return uint64_t(buffer->buffer); - }; break; + } 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; + } 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; + } break; default: { // not supported for this driver return 0; - }; break; + } break; } } @@ -9161,7 +9327,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; @@ -9305,6 +9471,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()) { diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index cf0b725cfc..7d9bd19309 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 */ @@ -175,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 @@ -219,7 +219,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } }; - Error _buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mapping); + Error _buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mem_usage, VmaAllocationCreateFlags p_mem_flags); Error _buffer_free(Buffer *p_buffer); Error _buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer = false, uint32_t p_required_align = 32); @@ -1016,6 +1016,8 @@ 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; @@ -1036,8 +1038,9 @@ 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); @@ -1200,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); @@ -1225,6 +1228,7 @@ 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); diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 44fed02f41..a09a757842 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,6 +33,7 @@ #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" @@ -41,11 +42,12 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <vector> #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, @@ -137,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) { @@ -175,7 +177,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_report_callback( const char *pMessage, void *pUserData) { String debugMessage = String("Vulkan Debug Report: object - ") + - String::num_int64(object) + "\n" + pMessage; + String::num_int64(object) + "\n" + pMessage; switch (flags) { case VK_DEBUG_REPORT_DEBUG_BIT_EXT: @@ -212,7 +214,7 @@ VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char *const *c } Error VulkanContext::_get_preferred_validation_layers(uint32_t *count, const char *const **names) { - static const std::vector<std::vector<const char *>> instance_validation_layers_alt{ + static const LocalVector<LocalVector<const char *>> instance_validation_layers_alt{ // Preferred set of validation layers { "VK_LAYER_KHRONOS_validation" }, @@ -249,10 +251,10 @@ Error VulkanContext::_get_preferred_validation_layers(uint32_t *count, const cha } 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].data(), instance_layer_count, instance_layers)) { + 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].data(); + *names = instance_validation_layers_alt[i].ptr(); } break; } @@ -273,9 +275,9 @@ Error VulkanContext::_obtain_vulkan_version() { uint32_t api_version; VkResult res = func(&api_version); if (res == VK_SUCCESS) { - vulkan_major = VK_VERSION_MAJOR(api_version); - vulkan_minor = VK_VERSION_MINOR(api_version); - vulkan_patch = VK_VERSION_PATCH(api_version); + 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 @@ -514,27 +516,62 @@ Error VulkanContext::_check_capabilities() { 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 device_features_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2"); - if (device_features_func == nullptr) { + 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 - device_features_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); + vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); } - if (device_features_func != nullptr) { + if (vkGetPhysicalDeviceFeatures2_func != nullptr) { // check our extended features - VkPhysicalDeviceMultiviewFeatures multiview_features; - multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; - multiview_features.pNext = nullptr; + 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; - device_features_func(gpu, &device_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 @@ -597,7 +634,7 @@ Error VulkanContext::_check_capabilities() { return OK; } -Error VulkanContext::_create_physical_device() { +Error VulkanContext::_create_instance() { /* obtain version */ _obtain_vulkan_version(); @@ -610,15 +647,13 @@ 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, + /*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{}; @@ -631,10 +666,10 @@ Error VulkanContext::_create_physical_device() { } /* - * 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) { @@ -645,38 +680,44 @@ 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; + 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; @@ -684,10 +725,85 @@ Error VulkanContext::_create_physical_device() { 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" @@ -700,25 +816,136 @@ Error VulkanContext::_create_physical_device() { ERR_FAIL_V(ERR_CANT_CREATE); } - // 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. + static const struct { + uint32_t id; + const char *name; + } vendor_names[] = { + { 0x1002, "AMD" }, + { 0x1010, "ImgTec" }, + { 0x106B, "Apple" }, + { 0x10DE, "NVIDIA" }, + { 0x13B5, "ARM" }, + { 0x5143, "Qualcomm" }, + { 0x8086, "Intel" }, + { 0, nullptr }, + }; - // Default to first device - uint32_t device_index = 0; + int32_t device_index = -1; + if (vulkan_hooks) { + if (!vulkan_hooks->get_physical_device(&gpu)) { + return ERR_CANT_CREATE; + } - for (uint32_t i = 0; i < gpu_count; ++i) { - VkPhysicalDeviceProperties props; - vkGetPhysicalDeviceProperties(physical_devices[i], &props); + // 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 preferred 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; + } + } + } - if (props.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { - // Prefer discrete GPU. - 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]; } - gpu = physical_devices[device_index]; free(physical_devices); /* Look for device extensions */ @@ -730,20 +957,8 @@ Error VulkanContext::_create_physical_device() { /* Get identifier properties */ vkGetPhysicalDeviceProperties(gpu, &gpu_props); - static const struct { - uint32_t id; - const char *name; - } vendor_names[] = { - { 0x1002, "AMD" }, - { 0x1010, "ImgTec" }, - { 0x106B, "Apple" }, - { 0x10DE, "NVIDIA" }, - { 0x13B5, "ARM" }, - { 0x5143, "Qualcomm" }, - { 0x8086, "Intel" }, - { 0, nullptr }, - }; 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); { @@ -834,77 +1049,6 @@ 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; - } - } 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; - } - } - /* Call with nullptr data to get count */ vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, nullptr); ERR_FAIL_COND_V(queue_family_count == 0, ERR_CANT_CREATE); @@ -940,6 +1084,7 @@ Error VulkanContext::_create_physical_device() { } } + device_initialized = true; return OK; } @@ -954,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, @@ -965,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; @@ -977,39 +1173,15 @@ Error VulkanContext::_create_device() { sdevice.queueCreateInfoCount = 2; } - VkPhysicalDeviceVulkan11Features vulkan11features; - VkPhysicalDeviceMultiviewFeatures multiview_features; - if (vulkan_major > 1 || vulkan_minor >= 2) { - vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; - vulkan11features.pNext = nullptr; - // !BAS! Need to figure out which ones of these we want enabled... - vulkan11features.storageBuffer16BitAccess = 0; - vulkan11features.uniformAndStorageBuffer16BitAccess = 0; - vulkan11features.storagePushConstant16 = 0; - vulkan11features.storageInputOutput16 = 0; - 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; - - sdevice.pNext = &vulkan11features; - } else if (vulkan_major == 1 && vulkan_minor == 1) { - multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; - multiview_features.pNext = nullptr; - 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; - - sdevice.pNext = &multiview_features; + 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); } - err = vkCreateDevice(gpu, &sdevice, nullptr, &device); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - return OK; } @@ -1192,25 +1364,17 @@ bool VulkanContext::_use_validation_layers() { 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 // is created. Error err = _initialize_queues(p_surface); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - } else { - // make sure any of the surfaces supports present (validation layer complains if this is not done). - bool any_supports_present = false; - for (uint32_t i = 0; i < queue_family_count; i++) { - VkBool32 supports; - fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supports); - if (supports) { - any_supports_present = true; - break; - } - } - - ERR_FAIL_COND_V_MSG(!any_supports_present, ERR_CANT_CREATE, "Surface passed for sub-window creation does not support presenting"); } Window window; @@ -1253,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]; @@ -1265,7 +1435,11 @@ 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) { @@ -1677,12 +1851,12 @@ Error VulkanContext::initialize() { return FAILED; } #endif - Error err = _create_physical_device(); - if (err) { + + Error err = _create_instance(); + if (err != OK) { return err; } - device_initialized = true; return OK; } @@ -1835,24 +2009,25 @@ Error VulkanContext::swap_buffers() { } 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]; + 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++; } } - VkPipelineStageFlags pipe_stage_flags; 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 = 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; @@ -1868,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; @@ -2192,6 +2367,11 @@ 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; } diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index ae7c697be8..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,6 +37,7 @@ #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> @@ -44,6 +45,8 @@ #include <vulkan/vulkan.h> #endif +#include "vulkan_hooks.h" + class VulkanContext { public: struct SubgroupCapabilities { @@ -66,6 +69,18 @@ public: 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, @@ -73,6 +88,7 @@ private: FRAME_LAG = 2 }; + static VulkanHooks *vulkan_hooks; VkInstance inst = VK_NULL_HANDLE; VkPhysicalDevice gpu = VK_NULL_HANDLE; VkPhysicalDeviceProperties gpu_props; @@ -88,9 +104,12 @@ private: 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; @@ -212,7 +231,9 @@ private: const char *pMessage, void *pUserData); - Error _create_physical_device(); + Error _create_instance(); + + Error _create_physical_device(VkSurfaceKHR p_surface); Error _initialize_queues(VkSurfaceKHR p_surface); @@ -239,6 +260,8 @@ public: 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(); @@ -247,9 +270,12 @@ public: 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); @@ -278,6 +304,7 @@ 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); 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 3b2f078120..3a62850339 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 */ @@ -121,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; @@ -162,7 +168,7 @@ public: HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) { return S_OK; - }; + } HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) { return S_OK; @@ -189,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) { @@ -373,18 +383,21 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c 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; - HRESULT hr = p_device->audio_client->GetBufferSize(&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 - buffer_frames = max_frames; - - 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; + if (!p_capture) { + buffer_frames = max_frames; + + 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; @@ -441,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 @@ -472,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; @@ -665,7 +680,7 @@ void AudioDriverWASAPI::write_sample(WORD format_tag, int bits_per_sample, BYTE } void AudioDriverWASAPI::thread_func(void *p_udata) { - AudioDriverWASAPI *ad = (AudioDriverWASAPI *)p_udata; + AudioDriverWASAPI *ad = static_cast<AudioDriverWASAPI *>(p_udata); uint32_t avail_frames = 0; uint32_t write_ofs = 0; diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index 6df9b4d2ee..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 */ diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 3e98e36d14..881575d245 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 */ @@ -133,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)) { @@ -165,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(); @@ -182,9 +185,9 @@ Error DirAccessWindows::make_dir(String p_dir) { return ERR_CANT_CREATE; } -String DirAccessWindows::get_current_dir(bool p_include_drive) { +String DirAccessWindows::get_current_dir(bool p_include_drive) const { 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()); @@ -196,7 +199,7 @@ 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.substr(p + 1); @@ -349,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 1ba4e70e42..fbb07ddef8 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 */ @@ -35,10 +35,6 @@ #include "core/io/dir_access.h" -/** - @author Juan Linietsky <reduz@gmail.com> -*/ - struct DirAccessWindowsPrivate; class DirAccessWindows : public DirAccess { @@ -68,7 +64,7 @@ public: virtual String get_drive(int p_drive); virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success - virtual String get_current_dir(bool p_include_drive = true); ///< return current dir location + virtual String get_current_dir(bool p_include_drive = true) const; ///< return current dir location virtual bool file_exists(String p_file); virtual bool dir_exists(String p_dir); @@ -90,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 035c44a28c..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 */ @@ -87,7 +87,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { 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 @@ -99,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."); @@ -142,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) { @@ -269,7 +269,7 @@ uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const uint64_t read = fread(p_dst, 1, p_length, f); check_errors(); return read; -}; +} Error FileAccessWindows::get_error() const { return last_error; @@ -326,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); @@ -335,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 7280fc3237..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 */ 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 2f691489b3..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 */ diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index 1c7bf5d6c6..c32c7cf1e5 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 */ @@ -84,7 +84,7 @@ Error AudioDriverXAudio2::init() { } void AudioDriverXAudio2::thread_func(void *p_udata) { - AudioDriverXAudio2 *ad = (AudioDriverXAudio2 *)p_udata; + AudioDriverXAudio2 *ad = static_cast<AudioDriverXAudio2 *>(p_udata); while (!ad->exit_thread) { if (!ad->active) { diff --git a/drivers/xaudio2/audio_driver_xaudio2.h b/drivers/xaudio2/audio_driver_xaudio2.h index c1910d3f5d..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 */ |