summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/SCsub2
-rw-r--r--drivers/alsa/audio_driver_alsa.cpp2
-rw-r--r--drivers/backtrace/SCsub44
-rw-r--r--drivers/d3d12/rendering_context_driver_d3d12.cpp1
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp4
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.h1
-rw-r--r--drivers/egl/egl_manager.cpp17
-rw-r--r--drivers/egl/egl_manager.h1
-rw-r--r--drivers/gles3/effects/copy_effects.cpp6
-rw-r--r--drivers/gles3/effects/cubemap_filter.cpp209
-rw-r--r--drivers/gles3/effects/cubemap_filter.h70
-rw-r--r--drivers/gles3/effects/post_effects.cpp4
-rw-r--r--drivers/gles3/effects/post_effects.h2
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp2
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp9
-rw-r--r--drivers/gles3/rasterizer_gles3.h2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp468
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h17
-rw-r--r--drivers/gles3/shaders/SCsub1
-rw-r--r--drivers/gles3/shaders/canvas.glsl26
-rw-r--r--drivers/gles3/shaders/cube_to_dp.glsl4
-rw-r--r--drivers/gles3/shaders/effects/cubemap_filter.glsl (renamed from drivers/gles3/shaders/cubemap_filter.glsl)0
-rw-r--r--drivers/gles3/shaders/effects/post.glsl42
-rw-r--r--drivers/gles3/shaders/particles.glsl3
-rw-r--r--drivers/gles3/shaders/particles_copy.glsl77
-rw-r--r--drivers/gles3/shaders/scene.glsl225
-rw-r--r--drivers/gles3/shaders/sky.glsl14
-rw-r--r--drivers/gles3/shaders/stdlib_inc.glsl33
-rw-r--r--drivers/gles3/shaders/tonemap.glsl333
-rw-r--r--drivers/gles3/shaders/tonemap_inc.glsl67
-rw-r--r--drivers/gles3/storage/config.cpp2
-rw-r--r--drivers/gles3/storage/light_storage.cpp507
-rw-r--r--drivers/gles3/storage/light_storage.h100
-rw-r--r--drivers/gles3/storage/material_storage.cpp13
-rw-r--r--drivers/gles3/storage/material_storage.h3
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp19
-rw-r--r--drivers/gles3/storage/particles_storage.cpp20
-rw-r--r--drivers/gles3/storage/render_scene_buffers_gles3.cpp10
-rw-r--r--drivers/gles3/storage/render_scene_buffers_gles3.h3
-rw-r--r--drivers/gles3/storage/texture_storage.cpp26
-rw-r--r--drivers/gles3/storage/texture_storage.h2
-rw-r--r--drivers/gles3/storage/utilities.cpp18
-rw-r--r--drivers/png/SCsub2
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp4
-rw-r--r--drivers/unix/dir_access_unix.cpp6
-rw-r--r--drivers/unix/file_access_unix.cpp17
-rw-r--r--drivers/unix/file_access_unix.h1
-rw-r--r--drivers/unix/file_access_unix_pipe.cpp185
-rw-r--r--drivers/unix/file_access_unix_pipe.h97
-rw-r--r--drivers/unix/os_unix.cpp163
-rw-r--r--drivers/unix/os_unix.h11
-rw-r--r--drivers/vulkan/rendering_context_driver_vulkan.cpp28
-rw-r--r--drivers/vulkan/rendering_context_driver_vulkan.h1
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.cpp12
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.h1
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp55
-rw-r--r--drivers/wasapi/audio_driver_wasapi.h9
-rw-r--r--drivers/windows/dir_access_windows.cpp60
-rw-r--r--drivers/windows/dir_access_windows.h6
-rw-r--r--drivers/windows/file_access_windows.cpp84
-rw-r--r--drivers/windows/file_access_windows.h1
-rw-r--r--drivers/windows/file_access_windows_pipe.cpp159
-rw-r--r--drivers/windows/file_access_windows_pipe.h96
63 files changed, 2522 insertions, 885 deletions
diff --git a/drivers/SCsub b/drivers/SCsub
index 9c8b16d3e5..e77b96cc87 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -14,6 +14,8 @@ SConscript("coreaudio/SCsub")
SConscript("pulseaudio/SCsub")
if env["platform"] == "windows":
SConscript("wasapi/SCsub")
+ if not env.msvc:
+ SConscript("backtrace/SCsub")
if env["xaudio2"]:
SConscript("xaudio2/SCsub")
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp
index 764d66c3b8..0c9635339b 100644
--- a/drivers/alsa/audio_driver_alsa.cpp
+++ b/drivers/alsa/audio_driver_alsa.cpp
@@ -52,7 +52,7 @@ Error AudioDriverALSA::init_output_device() {
// If there is a specified output device check that it is really present
if (output_device_name != "Default") {
PackedStringArray list = get_output_device_list();
- if (list.find(output_device_name) == -1) {
+ if (!list.has(output_device_name)) {
output_device_name = "Default";
new_output_device = "Default";
}
diff --git a/drivers/backtrace/SCsub b/drivers/backtrace/SCsub
new file mode 100644
index 0000000000..f61fb21581
--- /dev/null
+++ b/drivers/backtrace/SCsub
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env_backtrace = env.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+thirdparty_dir = "#thirdparty/libbacktrace/"
+thirdparty_sources = [
+ "atomic.c",
+ "dwarf.c",
+ "fileline.c",
+ "posix.c",
+ "print.c",
+ "sort.c",
+ "state.c",
+ "backtrace.c",
+ "simple.c",
+ "pecoff.c",
+ "read.c",
+ "alloc.c",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_backtrace.Prepend(CPPPATH=[thirdparty_dir])
+
+env_thirdparty = env_backtrace.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+
+env.drivers_sources += thirdparty_obj
+
+# Godot source files
+
+driver_obj = []
+
+env_backtrace.add_source_files(driver_obj, "*.cpp")
+env.drivers_sources += driver_obj
+
+# Needed to force rebuilding the driver files when the thirdparty library is updated.
+env.Depends(driver_obj, thirdparty_obj)
diff --git a/drivers/d3d12/rendering_context_driver_d3d12.cpp b/drivers/d3d12/rendering_context_driver_d3d12.cpp
index 726be064bd..128b8bcd03 100644
--- a/drivers/d3d12/rendering_context_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_context_driver_d3d12.cpp
@@ -173,6 +173,7 @@ Error RenderingContextDriverD3D12::_initialize_devices() {
Device &device = driver_devices[i];
device.name = desc.Description;
device.vendor = Vendor(desc.VendorId);
+ device.workarounds = Workarounds();
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
device.type = DEVICE_TYPE_CPU;
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index fc7aa62aae..9407826ebf 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -5912,6 +5912,9 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) {
case LIMIT_VRS_TEXEL_WIDTH:
case LIMIT_VRS_TEXEL_HEIGHT:
return vrs_capabilities.ss_image_tile_size;
+ case LIMIT_VRS_MAX_FRAGMENT_WIDTH:
+ case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
+ return vrs_capabilities.ss_max_fragment_size;
default: {
#ifdef DEV_ENABLED
WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + ".");
@@ -6218,6 +6221,7 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() {
vrs_capabilities.primitive_in_multiviewport = options6.PerPrimitiveShadingRateSupportedWithViewportIndexing;
vrs_capabilities.ss_image_supported = true;
vrs_capabilities.ss_image_tile_size = options6.ShadingRateImageTileSize;
+ vrs_capabilities.ss_max_fragment_size = 8; // TODO figure out if this is supplied and/or needed
vrs_capabilities.additional_rates_supported = options6.AdditionalShadingRatesSupported;
}
}
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h
index 61da9a5169..8e1223bdaa 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.h
+++ b/drivers/d3d12/rendering_device_driver_d3d12.h
@@ -105,6 +105,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
bool primitive_in_multiviewport = false;
bool ss_image_supported = false; // We can provide a density map attachment on our framebuffer.
uint32_t ss_image_tile_size = 0;
+ uint32_t ss_max_fragment_size = 0;
bool additional_rates_supported = false;
};
diff --git a/drivers/egl/egl_manager.cpp b/drivers/egl/egl_manager.cpp
index 10c2119260..6073856747 100644
--- a/drivers/egl/egl_manager.cpp
+++ b/drivers/egl/egl_manager.cpp
@@ -260,21 +260,6 @@ void EGLManager::release_current() {
eglMakeCurrent(current_display.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
-void EGLManager::make_current() {
- if (!current_window) {
- return;
- }
-
- if (!current_window->initialized) {
- WARN_PRINT("Current OpenGL window is uninitialized!");
- return;
- }
-
- GLDisplay &current_display = displays[current_window->gldisplay_id];
-
- eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
-}
-
void EGLManager::swap_buffers() {
if (!current_window) {
return;
@@ -398,7 +383,7 @@ Error EGLManager::initialize() {
ERR_FAIL_COND_V(eglGetError() != EGL_SUCCESS, ERR_BUG);
const char *platform = _get_platform_extension_name();
- if (extensions_string.split(" ").find(platform) < 0) {
+ if (!extensions_string.split(" ").has(platform)) {
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, vformat("EGL platform extension \"%s\" not found.", platform));
}
diff --git a/drivers/egl/egl_manager.h b/drivers/egl/egl_manager.h
index 61d3289b2d..83779349f0 100644
--- a/drivers/egl/egl_manager.h
+++ b/drivers/egl/egl_manager.h
@@ -98,7 +98,6 @@ public:
void window_destroy(DisplayServer::WindowID p_window_id);
void release_current();
- void make_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp
index 6e64652982..47ca832bd7 100644
--- a/drivers/gles3/effects/copy_effects.cpp
+++ b/drivers/gles3/effects/copy_effects.cpp
@@ -198,8 +198,7 @@ void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, con
for (int i = 1; i < p_mipmap_count; i++) {
dest_region.position.x >>= 1;
dest_region.position.y >>= 1;
- dest_region.size.x = MAX(1, dest_region.size.x >> 1);
- dest_region.size.y = MAX(1, dest_region.size.y >> 1);
+ dest_region.size = Size2i(dest_region.size.x >> 1, dest_region.size.y >> 1).maxi(1);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[i % 2]);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i);
glBlitFramebuffer(source_region.position.x, source_region.position.y, source_region.position.x + source_region.size.x, source_region.position.y + source_region.size.y,
@@ -238,8 +237,7 @@ void CopyEffects::gaussian_blur(GLuint p_source_texture, int p_mipmap_count, con
for (int i = 1; i < p_mipmap_count; i++) {
dest_region.position.x >>= 1;
dest_region.position.y >>= 1;
- dest_region.size.x = MAX(1, dest_region.size.x >> 1);
- dest_region.size.y = MAX(1, dest_region.size.y >> 1);
+ dest_region.size = Size2i(dest_region.size.x >> 1, dest_region.size.y >> 1).maxi(1);
base_size.x >>= 1;
base_size.y >>= 1;
diff --git a/drivers/gles3/effects/cubemap_filter.cpp b/drivers/gles3/effects/cubemap_filter.cpp
new file mode 100644
index 0000000000..b88e655492
--- /dev/null
+++ b/drivers/gles3/effects/cubemap_filter.cpp
@@ -0,0 +1,209 @@
+/**************************************************************************/
+/* cubemap_filter.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifdef GLES3_ENABLED
+
+#include "cubemap_filter.h"
+
+#include "../storage/texture_storage.h"
+#include "core/config/project_settings.h"
+
+using namespace GLES3;
+
+CubemapFilter *CubemapFilter::singleton = nullptr;
+
+CubemapFilter::CubemapFilter() {
+ singleton = this;
+ ggx_samples = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
+
+ {
+ String defines;
+ defines += "\n#define MAX_SAMPLE_COUNT " + itos(ggx_samples) + "\n";
+ cubemap_filter.shader.initialize(defines);
+ cubemap_filter.shader_version = cubemap_filter.shader.version_create();
+ }
+
+ { // Screen Triangle.
+ glGenBuffers(1, &screen_triangle);
+ glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
+
+ const float qv[6] = {
+ -1.0f,
+ -1.0f,
+ 3.0f,
+ -1.0f,
+ -1.0f,
+ 3.0f,
+ };
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+
+ glGenVertexArrays(1, &screen_triangle_array);
+ glBindVertexArray(screen_triangle_array);
+ glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ }
+}
+
+CubemapFilter::~CubemapFilter() {
+ glDeleteBuffers(1, &screen_triangle);
+ glDeleteVertexArrays(1, &screen_triangle_array);
+
+ cubemap_filter.shader.version_free(cubemap_filter.shader_version);
+ singleton = nullptr;
+}
+
+// Helper functions for IBL filtering
+
+Vector3 importance_sample_GGX(Vector2 xi, float roughness4) {
+ // Compute distribution direction
+ float phi = 2.0 * Math_PI * xi.x;
+ float cos_theta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
+ float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
+
+ // Convert to spherical direction
+ Vector3 half_vector;
+ half_vector.x = sin_theta * cos(phi);
+ half_vector.y = sin_theta * sin(phi);
+ half_vector.z = cos_theta;
+
+ return half_vector;
+}
+
+float distribution_GGX(float NdotH, float roughness4) {
+ float NdotH2 = NdotH * NdotH;
+ float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
+ denom = Math_PI * denom * denom;
+
+ return roughness4 / denom;
+}
+
+float radical_inverse_vdC(uint32_t bits) {
+ bits = (bits << 16) | (bits >> 16);
+ bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1);
+ bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2);
+ bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4);
+ bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8);
+
+ return float(bits) * 2.3283064365386963e-10;
+}
+
+Vector2 hammersley(uint32_t i, uint32_t N) {
+ return Vector2(float(i) / float(N), radical_inverse_vdC(i));
+}
+
+void CubemapFilter::filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubemap, GLuint p_dest_framebuffer, int p_source_size, int p_mipmap_count, int p_layer) {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, p_source_cubemap);
+ glBindFramebuffer(GL_FRAMEBUFFER, p_dest_framebuffer);
+
+ CubemapFilterShaderGLES3::ShaderVariant mode = CubemapFilterShaderGLES3::MODE_DEFAULT;
+
+ if (p_layer == 0) {
+ glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+ // Copy over base layer without filtering.
+ mode = CubemapFilterShaderGLES3::MODE_COPY;
+ }
+
+ int size = p_source_size >> p_layer;
+ glViewport(0, 0, size, size);
+ glBindVertexArray(screen_triangle_array);
+
+ bool success = cubemap_filter.shader.version_bind_shader(cubemap_filter.shader_version, mode);
+ if (!success) {
+ return;
+ }
+
+ if (p_layer > 0) {
+ const uint32_t sample_counts[4] = { 1, ggx_samples / 4, ggx_samples / 2, ggx_samples };
+ uint32_t sample_count = sample_counts[MIN(3, p_layer)];
+
+ float roughness = float(p_layer) / (p_mipmap_count);
+ float roughness4 = roughness * roughness;
+ roughness4 *= roughness4;
+
+ float solid_angle_texel = 4.0 * Math_PI / float(6 * size * size);
+
+ LocalVector<float> sample_directions;
+ sample_directions.resize(4 * sample_count);
+
+ uint32_t index = 0;
+ float weight = 0.0;
+ for (uint32_t i = 0; i < sample_count; i++) {
+ Vector2 xi = hammersley(i, sample_count);
+ Vector3 dir = importance_sample_GGX(xi, roughness4);
+ Vector3 light_vec = (2.0 * dir.z * dir - Vector3(0.0, 0.0, 1.0));
+
+ if (light_vec.z < 0.0) {
+ continue;
+ }
+
+ sample_directions[index * 4] = light_vec.x;
+ sample_directions[index * 4 + 1] = light_vec.y;
+ sample_directions[index * 4 + 2] = light_vec.z;
+
+ float D = distribution_GGX(dir.z, roughness4);
+ float pdf = D * dir.z / (4.0 * dir.z) + 0.0001;
+
+ float solid_angle_sample = 1.0 / (float(sample_count) * pdf + 0.0001);
+
+ float mip_level = MAX(0.5 * log2(solid_angle_sample / solid_angle_texel) + float(MAX(1, p_layer - 3)), 1.0);
+
+ sample_directions[index * 4 + 3] = mip_level;
+ weight += light_vec.z;
+ index++;
+ }
+
+ glUniform4fv(cubemap_filter.shader.version_get_uniform(CubemapFilterShaderGLES3::SAMPLE_DIRECTIONS_MIP, cubemap_filter.shader_version, mode), sample_count, sample_directions.ptr());
+ cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::WEIGHT, weight, cubemap_filter.shader_version, mode);
+ cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::SAMPLE_COUNT, index, cubemap_filter.shader_version, mode);
+ }
+
+ for (int i = 0; i < 6; i++) {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, p_dest_cubemap, p_layer);
+#ifdef DEBUG_ENABLED
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ WARN_PRINT("Could not bind sky radiance face: " + itos(i) + ", status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
+ }
+#endif
+ cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_ID, i, cubemap_filter.shader_version, mode);
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ }
+ glBindVertexArray(0);
+ glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+}
+
+#endif // GLES3_ENABLED
diff --git a/drivers/gles3/effects/cubemap_filter.h b/drivers/gles3/effects/cubemap_filter.h
new file mode 100644
index 0000000000..eaaa6f4075
--- /dev/null
+++ b/drivers/gles3/effects/cubemap_filter.h
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* cubemap_filter.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef CUBEMAP_FILTER_GLES3_H
+#define CUBEMAP_FILTER_GLES3_H
+
+#ifdef GLES3_ENABLED
+
+#include "drivers/gles3/shaders/effects/cubemap_filter.glsl.gen.h"
+
+namespace GLES3 {
+
+class CubemapFilter {
+private:
+ struct CMF {
+ CubemapFilterShaderGLES3 shader;
+ RID shader_version;
+ } cubemap_filter;
+
+ static CubemapFilter *singleton;
+
+ // Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal.
+ GLuint screen_triangle = 0;
+ GLuint screen_triangle_array = 0;
+
+ uint32_t ggx_samples = 128;
+
+public:
+ static CubemapFilter *get_singleton() {
+ return singleton;
+ }
+
+ CubemapFilter();
+ ~CubemapFilter();
+
+ void filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubemap, GLuint p_dest_framebuffer, int p_source_size, int p_mipmap_count, int p_layer);
+};
+
+} //namespace GLES3
+
+#endif // GLES3_ENABLED
+
+#endif // CUBEMAP_FILTER_GLES3_H
diff --git a/drivers/gles3/effects/post_effects.cpp b/drivers/gles3/effects/post_effects.cpp
index 8ad872f319..105c8f6b71 100644
--- a/drivers/gles3/effects/post_effects.cpp
+++ b/drivers/gles3/effects/post_effects.cpp
@@ -87,7 +87,7 @@ void PostEffects::_draw_screen_triangle() {
glBindVertexArray(0);
}
-void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view, bool p_use_multiview) {
+void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view, bool p_use_multiview, uint64_t p_spec_constants) {
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDisable(GL_BLEND);
@@ -96,7 +96,7 @@ void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuin
glViewport(0, 0, p_dest_size.x, p_dest_size.y);
PostShaderGLES3::ShaderVariant mode = PostShaderGLES3::MODE_DEFAULT;
- uint64_t flags = 0;
+ uint64_t flags = p_spec_constants;
if (p_use_multiview) {
flags |= PostShaderGLES3::USE_MULTIVIEW;
}
diff --git a/drivers/gles3/effects/post_effects.h b/drivers/gles3/effects/post_effects.h
index b90c77d6c7..916d29a052 100644
--- a/drivers/gles3/effects/post_effects.h
+++ b/drivers/gles3/effects/post_effects.h
@@ -59,7 +59,7 @@ public:
PostEffects();
~PostEffects();
- void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false);
+ void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0);
};
} //namespace GLES3
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 9fa95a93f8..5fabeb94f5 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -1922,7 +1922,7 @@ void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstanc
while (instance) {
OccluderPolygon *oc = occluder_polygon_owner.get_or_null(instance->occluder);
- if (!oc || oc->sdf_vertex_array == 0) {
+ if (!oc || oc->sdf_vertex_array == 0 || !instance->sdf_collision) {
instance = instance->next;
continue;
}
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index cee4f93b3d..6e7d4a6733 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -208,6 +208,7 @@ void RasterizerGLES3::finalize() {
memdelete(fog);
memdelete(post_effects);
memdelete(glow);
+ memdelete(cubemap_filter);
memdelete(copy_effects);
memdelete(light_storage);
memdelete(particles_storage);
@@ -354,6 +355,7 @@ RasterizerGLES3::RasterizerGLES3() {
particles_storage = memnew(GLES3::ParticlesStorage);
light_storage = memnew(GLES3::LightStorage);
copy_effects = memnew(GLES3::CopyEffects);
+ cubemap_filter = memnew(GLES3::CubemapFilter);
glow = memnew(GLES3::Glow);
post_effects = memnew(GLES3::PostEffects);
gi = memnew(GLES3::GI);
@@ -393,10 +395,13 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
if (p_first) {
- Size2i win_size = DisplayServer::get_singleton()->window_get_size();
if (p_screen_rect.position != Vector2() || p_screen_rect.size != rt->size) {
// Viewport doesn't cover entire window so clear window to black before blitting.
- glViewport(0, 0, win_size.width, win_size.height);
+ // Querying the actual window size from the DisplayServer would deadlock in separate render thread mode,
+ // so let's set the biggest viewport the implementation supports, to be sure the window is fully covered.
+ GLsizei max_vp[2] = {};
+ glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_vp);
+ glViewport(0, 0, max_vp[0], max_vp[1]);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h
index 8d52dc2365..09d3c7bd52 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -34,6 +34,7 @@
#ifdef GLES3_ENABLED
#include "effects/copy_effects.h"
+#include "effects/cubemap_filter.h"
#include "effects/glow.h"
#include "effects/post_effects.h"
#include "environment/fog.h"
@@ -70,6 +71,7 @@ protected:
GLES3::GI *gi = nullptr;
GLES3::Fog *fog = nullptr;
GLES3::CopyEffects *copy_effects = nullptr;
+ GLES3::CubemapFilter *cubemap_filter = nullptr;
GLES3::Glow *glow = nullptr;
GLES3::PostEffects *post_effects = nullptr;
RasterizerCanvasGLES3 *canvas = nullptr;
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 29cfa251d6..03f947cd05 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -65,7 +65,7 @@ RenderGeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_bas
}
uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() {
- return (1 << RS::INSTANCE_LIGHT);
+ return ((1 << RS::INSTANCE_LIGHT) | (1 << RS::INSTANCE_REFLECTION_PROBE));
}
void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) {
@@ -97,6 +97,14 @@ void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instances(const RID
}
}
+void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) {
+ paired_reflection_probes.clear();
+
+ for (uint32_t i = 0; i < p_reflection_probe_instance_count; i++) {
+ paired_reflection_probes.push_back(p_reflection_probe_instances[i]);
+ }
+}
+
void RasterizerSceneGLES3::geometry_instance_free(RenderGeometryInstance *p_geometry_instance) {
GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
ERR_FAIL_NULL(ginstance);
@@ -780,7 +788,6 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
}
if (!p_apply_color_adjustments_in_post) {
spec_constants |= SkyShaderGLES3::APPLY_TONEMAPPING;
- // TODO add BCS and color corrections once supported.
}
RS::EnvironmentBG background = environment_get_background(p_env);
@@ -823,6 +830,11 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
} else {
camera = p_projection;
}
+
+ Projection correction;
+ correction.set_depth_correction(false, true, false);
+ camera = correction * camera;
+
Basis sky_transform = environment_get_sky_orientation(p_env);
sky_transform.invert();
sky_transform = sky_transform * p_transform.basis;
@@ -849,6 +861,7 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
}
void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier) {
+ GLES3::CubemapFilter *cubemap_filter = GLES3::CubemapFilter::get_singleton();
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
ERR_FAIL_COND(p_env.is_null());
@@ -933,7 +946,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
Projection cm;
cm.set_perspective(90, 1, 0.01, 10.0);
Projection correction;
- correction.columns[1][1] = -1.0;
+ correction.set_depth_correction(true, true, false);
cm = correction * cm;
bool success = material_storage->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_CUBEMAP);
@@ -965,10 +978,10 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
if (update_single_frame) {
for (int i = 0; i < max_processing_layer; i++) {
- _filter_sky_radiance(sky, i);
+ cubemap_filter->filter_radiance(sky->raw_radiance, sky->radiance, sky->radiance_framebuffer, sky->radiance_size, sky->mipmap_count, i);
}
} else {
- _filter_sky_radiance(sky, 0); //Just copy over the first mipmap
+ cubemap_filter->filter_radiance(sky->raw_radiance, sky->radiance, sky->radiance_framebuffer, sky->radiance_size, sky->mipmap_count, 0); // Just copy over the first mipmap.
}
sky->processing_layer = 1;
sky->baked_exposure = p_sky_energy_multiplier;
@@ -979,135 +992,11 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
scene_state.enable_gl_blend(false);
- _filter_sky_radiance(sky, sky->processing_layer);
+ cubemap_filter->filter_radiance(sky->raw_radiance, sky->radiance, sky->radiance_framebuffer, sky->radiance_size, sky->mipmap_count, sky->processing_layer);
sky->processing_layer++;
}
}
-}
-
-// Helper functions for IBL filtering
-
-Vector3 importance_sample_GGX(Vector2 xi, float roughness4) {
- // Compute distribution direction
- float phi = 2.0 * Math_PI * xi.x;
- float cos_theta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
- float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
-
- // Convert to spherical direction
- Vector3 half_vector;
- half_vector.x = sin_theta * cos(phi);
- half_vector.y = sin_theta * sin(phi);
- half_vector.z = cos_theta;
-
- return half_vector;
-}
-
-float distribution_GGX(float NdotH, float roughness4) {
- float NdotH2 = NdotH * NdotH;
- float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
- denom = Math_PI * denom * denom;
-
- return roughness4 / denom;
-}
-
-float radical_inverse_vdC(uint32_t bits) {
- bits = (bits << 16) | (bits >> 16);
- bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1);
- bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2);
- bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4);
- bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8);
-
- return float(bits) * 2.3283064365386963e-10;
-}
-
-Vector2 hammersley(uint32_t i, uint32_t N) {
- return Vector2(float(i) / float(N), radical_inverse_vdC(i));
-}
-
-void RasterizerSceneGLES3::_filter_sky_radiance(Sky *p_sky, int p_base_layer) {
- GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_CUBE_MAP, p_sky->raw_radiance);
- glBindFramebuffer(GL_FRAMEBUFFER, p_sky->radiance_framebuffer);
-
- CubemapFilterShaderGLES3::ShaderVariant mode = CubemapFilterShaderGLES3::MODE_DEFAULT;
-
- if (p_base_layer == 0) {
- glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
- // Copy over base layer without filtering.
- mode = CubemapFilterShaderGLES3::MODE_COPY;
- }
-
- int size = p_sky->radiance_size >> p_base_layer;
- glViewport(0, 0, size, size);
- glBindVertexArray(sky_globals.screen_triangle_array);
-
- bool success = material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, mode);
- if (!success) {
- return;
- }
-
- if (p_base_layer > 0) {
- const uint32_t sample_counts[4] = { 1, sky_globals.ggx_samples / 4, sky_globals.ggx_samples / 2, sky_globals.ggx_samples };
- uint32_t sample_count = sample_counts[MIN(3, p_base_layer)];
-
- float roughness = float(p_base_layer) / (p_sky->mipmap_count);
- float roughness4 = roughness * roughness;
- roughness4 *= roughness4;
-
- float solid_angle_texel = 4.0 * Math_PI / float(6 * size * size);
-
- LocalVector<float> sample_directions;
- sample_directions.resize(4 * sample_count);
-
- uint32_t index = 0;
- float weight = 0.0;
- for (uint32_t i = 0; i < sample_count; i++) {
- Vector2 xi = hammersley(i, sample_count);
- Vector3 dir = importance_sample_GGX(xi, roughness4);
- Vector3 light_vec = (2.0 * dir.z * dir - Vector3(0.0, 0.0, 1.0));
-
- if (light_vec.z < 0.0) {
- continue;
- }
-
- sample_directions[index * 4] = light_vec.x;
- sample_directions[index * 4 + 1] = light_vec.y;
- sample_directions[index * 4 + 2] = light_vec.z;
-
- float D = distribution_GGX(dir.z, roughness4);
- float pdf = D * dir.z / (4.0 * dir.z) + 0.0001;
-
- float solid_angle_sample = 1.0 / (float(sample_count) * pdf + 0.0001);
-
- float mip_level = MAX(0.5 * log2(solid_angle_sample / solid_angle_texel) + float(MAX(1, p_base_layer - 3)), 1.0);
-
- sample_directions[index * 4 + 3] = mip_level;
- weight += light_vec.z;
- index++;
- }
-
- glUniform4fv(material_storage->shaders.cubemap_filter_shader.version_get_uniform(CubemapFilterShaderGLES3::SAMPLE_DIRECTIONS_MIP, scene_globals.cubemap_filter_shader_version, mode), sample_count, sample_directions.ptr());
- material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::WEIGHT, weight, scene_globals.cubemap_filter_shader_version, mode);
- material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::SAMPLE_COUNT, index, scene_globals.cubemap_filter_shader_version, mode);
- }
-
- for (int i = 0; i < 6; i++) {
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, p_sky->radiance, p_base_layer);
-#ifdef DEBUG_ENABLED
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- WARN_PRINT("Could not bind sky radiance face: " + itos(i) + ", status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
- }
-#endif
- material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_ID, i, scene_globals.cubemap_filter_shader_version, mode);
-
- glDrawArrays(GL_TRIANGLES, 0, 3);
- }
- glBindVertexArray(0);
- glViewport(0, 0, p_sky->screen_size.x, p_sky->screen_size.y);
- glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+ glViewport(0, 0, sky->screen_size.x, sky->screen_size.y);
}
Ref<Image> RasterizerSceneGLES3::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) {
@@ -1329,6 +1218,7 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit
}
void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append) {
GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
+ GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton();
if (p_render_list == RENDER_LIST_OPAQUE) {
scene_state.used_screen_texture = false;
@@ -1387,22 +1277,24 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
inst->light_passes.clear();
inst->spot_light_gl_cache.clear();
inst->omni_light_gl_cache.clear();
+ inst->reflection_probes_local_transform_cache.clear();
+ inst->reflection_probe_rid_cache.clear();
uint64_t current_frame = RSG::rasterizer->get_frame_number();
if (inst->paired_omni_light_count) {
for (uint32_t j = 0; j < inst->paired_omni_light_count; j++) {
RID light_instance = inst->paired_omni_lights[j];
- if (GLES3::LightStorage::get_singleton()->light_instance_get_render_pass(light_instance) != current_frame) {
+ if (light_storage->light_instance_get_render_pass(light_instance) != current_frame) {
continue;
}
- RID light = GLES3::LightStorage::get_singleton()->light_instance_get_base_light(light_instance);
- int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance);
+ RID light = light_storage->light_instance_get_base_light(light_instance);
+ int32_t shadow_id = light_storage->light_instance_get_shadow_id(light_instance);
- if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) {
+ if (light_storage->light_has_shadow(light) && shadow_id >= 0) {
// Skip static lights when a lightmap is used.
- if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
+ if (!inst->lightmap_instance.is_valid() || light_storage->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
GeometryInstanceGLES3::LightPass pass;
- pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance);
+ pass.light_id = light_storage->light_instance_get_gl_id(light_instance);
pass.shadow_id = shadow_id;
pass.light_instance_rid = light_instance;
pass.is_omni = true;
@@ -1410,7 +1302,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
}
} else {
// Lights without shadow can all go in base pass.
- inst->omni_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance));
+ inst->omni_light_gl_cache.push_back((uint32_t)light_storage->light_instance_get_gl_id(light_instance));
}
}
}
@@ -1418,24 +1310,42 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
if (inst->paired_spot_light_count) {
for (uint32_t j = 0; j < inst->paired_spot_light_count; j++) {
RID light_instance = inst->paired_spot_lights[j];
- if (GLES3::LightStorage::get_singleton()->light_instance_get_render_pass(light_instance) != current_frame) {
+ if (light_storage->light_instance_get_render_pass(light_instance) != current_frame) {
continue;
}
- RID light = GLES3::LightStorage::get_singleton()->light_instance_get_base_light(light_instance);
- int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance);
+ RID light = light_storage->light_instance_get_base_light(light_instance);
+ int32_t shadow_id = light_storage->light_instance_get_shadow_id(light_instance);
- if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) {
+ if (light_storage->light_has_shadow(light) && shadow_id >= 0) {
// Skip static lights when a lightmap is used.
- if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
+ if (!inst->lightmap_instance.is_valid() || light_storage->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
GeometryInstanceGLES3::LightPass pass;
- pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance);
+ pass.light_id = light_storage->light_instance_get_gl_id(light_instance);
pass.shadow_id = shadow_id;
pass.light_instance_rid = light_instance;
inst->light_passes.push_back(pass);
}
} else {
// Lights without shadow can all go in base pass.
- inst->spot_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance));
+ inst->spot_light_gl_cache.push_back((uint32_t)light_storage->light_instance_get_gl_id(light_instance));
+ }
+ }
+ }
+
+ if (p_render_data->reflection_probe.is_null() && inst->paired_reflection_probes.size() > 0) {
+ // Do not include if we're rendering reflection probes.
+ // We only support two probes for now and we handle them first come, first serve.
+ // This should be improved one day, at minimum the list should be sorted by priority.
+
+ for (uint32_t pi = 0; pi < inst->paired_reflection_probes.size(); pi++) {
+ RID probe_instance = inst->paired_reflection_probes[pi];
+ RID atlas = light_storage->reflection_probe_instance_get_atlas(probe_instance);
+ RID probe = light_storage->reflection_probe_instance_get_probe(probe_instance);
+ uint32_t reflection_mask = light_storage->reflection_probe_get_reflection_mask(probe);
+ if (atlas.is_valid() && (inst->layer_mask & reflection_mask)) {
+ Transform3D local_matrix = p_render_data->inv_cam_transform * light_storage->reflection_probe_instance_get_transform(probe_instance);
+ inst->reflection_probes_local_transform_cache.push_back(local_matrix.affine_inverse());
+ inst->reflection_probe_rid_cache.push_back(probe_instance);
}
}
}
@@ -1546,7 +1456,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
// Needs to be called after _setup_lights so that directional_light_count is accurate.
void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows, float p_shadow_bias) {
Projection correction;
- correction.columns[1][1] = p_flip_y ? -1.0 : 1.0;
+ correction.set_depth_correction(p_flip_y, true, false);
Projection projection = correction * p_render_data->cam_projection;
//store camera into ubo
GLES3::MaterialStorage::store_camera(projection, scene_state.ubo.projection_matrix);
@@ -1801,7 +1711,9 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b
shadow_data.blend_splits = uint32_t((shadow_mode != RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL) && light_storage->light_directional_get_blend_splits(base));
for (int j = 0; j < 4; j++) {
Rect2 atlas_rect = li->shadow_transform[j].atlas_rect;
- Projection matrix = li->shadow_transform[j].camera;
+ Projection correction;
+ correction.set_depth_correction(false, true, false);
+ Projection matrix = correction * li->shadow_transform[j].camera;
float split = li->shadow_transform[MIN(limit, j)].split;
Projection bias;
@@ -2027,7 +1939,9 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b
Projection bias;
bias.set_light_bias();
- Projection cm = li->shadow_transform[0].camera;
+ Projection correction;
+ correction.set_depth_correction(false, true, false);
+ Projection cm = correction * li->shadow_transform[0].camera;
Projection shadow_mtx = bias * cm * modelview;
GLES3::MaterialStorage::store_camera(shadow_mtx, shadow_data.shadow_matrix);
}
@@ -2274,11 +2188,11 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas,
scene_state.reset_gl_state();
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_LESS);
+ glDepthFunc(GL_GREATER);
glColorMask(0, 0, 0, 0);
glDrawBuffers(0, nullptr);
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
if (needs_clear) {
glClear(GL_DEPTH_BUFFER_BIT);
}
@@ -2312,20 +2226,21 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
RENDER_TIMESTAMP("Setup 3D Scene");
bool apply_color_adjustments_in_post = false;
+ bool is_reflection_probe = p_reflection_probe.is_valid();
- Ref<RenderSceneBuffersGLES3> rb;
- if (p_render_buffers.is_valid()) {
- rb = p_render_buffers;
- ERR_FAIL_COND(rb.is_null());
+ Ref<RenderSceneBuffersGLES3> rb = p_render_buffers;
+ ERR_FAIL_COND(rb.is_null());
- if (rb->get_scaling_3d_mode() != RS::VIEWPORT_SCALING_3D_MODE_OFF) {
- // If we're scaling, we apply tonemapping etc. in post, so disable it during rendering
- apply_color_adjustments_in_post = true;
- }
+ if (rb->get_scaling_3d_mode() != RS::VIEWPORT_SCALING_3D_MODE_OFF) {
+ // If we're scaling, we apply tonemapping etc. in post, so disable it during rendering
+ apply_color_adjustments_in_post = true;
}
- GLES3::RenderTarget *rt = texture_storage->get_render_target(rb->render_target);
- ERR_FAIL_NULL(rt);
+ GLES3::RenderTarget *rt = nullptr; // No render target for reflection probe
+ if (!is_reflection_probe) {
+ rt = texture_storage->get_render_target(rb->render_target);
+ ERR_FAIL_NULL(rt);
+ }
bool glow_enabled = false;
if (p_environment.is_valid() && rb.is_valid()) {
@@ -2342,7 +2257,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
RenderDataGLES3 render_data;
{
render_data.render_buffers = rb;
- render_data.transparent_bg = rb.is_valid() ? rt->is_transparent : false;
+ render_data.transparent_bg = rt ? rt->is_transparent : false;
// Our first camera is used by default
render_data.cam_transform = p_camera_data->main_transform;
render_data.inv_cam_transform = render_data.cam_transform.affine_inverse();
@@ -2372,7 +2287,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
// this should be the same for all cameras..
render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier();
- if (rt->color_type == GL_UNSIGNED_INT_2_10_10_10_REV && glow_enabled) {
+ if (rt != nullptr && rt->color_type == GL_UNSIGNED_INT_2_10_10_10_REV && glow_enabled) {
// As our output is in sRGB and we're using 10bit color space, we can fake a little HDR to do glow...
render_data.luminance_multiplier = 0.25;
} else {
@@ -2406,7 +2321,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_GLOBALS_UNIFORM_LOCATION, global_buffer);
Color clear_color;
- if (p_render_buffers.is_valid()) {
+ if (!is_reflection_probe && rb->render_target.is_valid()) {
clear_color = texture_storage->render_target_get_clear_request_color(rb->render_target);
} else {
clear_color = texture_storage->get_default_clear_color();
@@ -2420,9 +2335,18 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
SceneState::TonemapUBO tonemap_ubo;
if (render_data.environment.is_valid()) {
+ bool use_bcs = environment_get_adjustments_enabled(render_data.environment);
+ if (use_bcs) {
+ apply_color_adjustments_in_post = true;
+ }
+
tonemap_ubo.exposure = environment_get_exposure(render_data.environment);
tonemap_ubo.white = environment_get_white(render_data.environment);
tonemap_ubo.tonemapper = int32_t(environment_get_tone_mapper(render_data.environment));
+
+ tonemap_ubo.brightness = environment_get_adjustments_brightness(render_data.environment);
+ tonemap_ubo.contrast = environment_get_adjustments_contrast(render_data.environment);
+ tonemap_ubo.saturation = environment_get_adjustments_saturation(render_data.environment);
}
if (scene_state.tonemap_buffer == 0) {
@@ -2439,9 +2363,9 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.ubo.emissive_exposure_normalization = -1.0; // Use default exposure normalization.
- bool flip_y = !render_data.reflection_probe.is_valid();
+ bool flip_y = !is_reflection_probe;
- if (rt->overridden.color.is_valid()) {
+ if (rt && rt->overridden.color.is_valid()) {
// If we've overridden the render target's color texture, then don't render upside down.
// We're probably rendering directly to an XR device.
flip_y = false;
@@ -2453,7 +2377,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
_render_shadows(&render_data, screen_size);
_setup_lights(&render_data, true, render_data.directional_light_count, render_data.omni_light_count, render_data.spot_light_count, render_data.directional_shadow_count);
- _setup_environment(&render_data, render_data.reflection_probe.is_valid(), screen_size, flip_y, clear_color, false);
+ _setup_environment(&render_data, is_reflection_probe, screen_size, flip_y, clear_color, false);
_fill_render_list(RENDER_LIST_OPAQUE, &render_data, PASS_MODE_COLOR);
render_list[RENDER_LIST_OPAQUE].sort_by_key();
@@ -2470,6 +2394,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
RS::EnvironmentBG bg_mode = environment_get_background(render_data.environment);
float bg_energy_multiplier = environment_get_bg_energy_multiplier(render_data.environment);
bg_energy_multiplier *= environment_get_bg_intensity(render_data.environment);
+ RS::EnvironmentReflectionSource reflection_source = environment_get_reflection_source(render_data.environment);
if (render_data.camera_attributes.is_valid()) {
bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(render_data.camera_attributes);
@@ -2480,7 +2405,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
clear_color.r *= bg_energy_multiplier;
clear_color.g *= bg_energy_multiplier;
clear_color.b *= bg_energy_multiplier;
- if (environment_get_fog_enabled(render_data.environment)) {
+ if (!render_data.transparent_bg && environment_get_fog_enabled(render_data.environment)) {
draw_sky_fog_only = true;
GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color));
}
@@ -2490,13 +2415,13 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
clear_color.r *= bg_energy_multiplier;
clear_color.g *= bg_energy_multiplier;
clear_color.b *= bg_energy_multiplier;
- if (environment_get_fog_enabled(render_data.environment)) {
+ if (!render_data.transparent_bg && environment_get_fog_enabled(render_data.environment)) {
draw_sky_fog_only = true;
GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color));
}
} break;
case RS::ENV_BG_SKY: {
- draw_sky = true;
+ draw_sky = !render_data.transparent_bg;
} break;
case RS::ENV_BG_CANVAS: {
keep_color = true;
@@ -2509,13 +2434,14 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
default: {
}
}
+
// setup sky if used for ambient, reflections, or background
- if (draw_sky || draw_sky_fog_only || environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY) {
+ if (draw_sky || draw_sky_fog_only || (reflection_source == RS::ENV_REFLECTION_SOURCE_BG && bg_mode == RS::ENV_BG_SKY) || reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY) {
RENDER_TIMESTAMP("Setup Sky");
Projection projection = render_data.cam_projection;
- if (render_data.reflection_probe.is_valid()) {
+ if (is_reflection_probe) {
Projection correction;
- correction.columns[1][1] = -1.0;
+ correction.set_depth_correction(true, true, false);
projection = correction * render_data.cam_projection;
}
@@ -2534,7 +2460,12 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
}
}
- GLuint fbo = rb->get_render_fbo();
+ GLuint fbo = 0;
+ if (is_reflection_probe) {
+ fbo = GLES3::LightStorage::get_singleton()->reflection_probe_instance_get_framebuffer(render_data.reflection_probe, render_data.reflection_probe_pass);
+ } else {
+ fbo = rb->get_render_fbo();
+ }
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, rb->internal_size.x, rb->internal_size.y);
@@ -2554,11 +2485,11 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
scene_state.enable_gl_blend(false);
- glDepthFunc(GL_LEQUAL);
+ glDepthFunc(GL_GEQUAL);
scene_state.enable_gl_scissor_test(false);
glColorMask(0, 0, 0, 0);
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
glClear(GL_DEPTH_BUFFER_BIT);
glDrawBuffers(0, nullptr);
@@ -2590,7 +2521,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.enable_gl_scissor_test(false);
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_LEQUAL);
+ glDepthFunc(GL_GEQUAL);
{
GLuint db = GL_COLOR_ATTACHMENT0;
@@ -2598,7 +2529,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
}
if (!fb_cleared) {
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
glClear(GL_DEPTH_BUFFER_BIT);
}
@@ -2637,8 +2568,6 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
if (!apply_color_adjustments_in_post) {
spec_constant_base_flags |= SceneShaderGLES3::APPLY_TONEMAPPING;
-
- // TODO add BCS and Color corrections here once supported.
}
}
// Render Opaque Objects.
@@ -2655,10 +2584,17 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.enable_gl_blend(false);
scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK);
- _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform, sky_energy_multiplier, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post);
+ Projection projection = render_data.cam_projection;
+ if (is_reflection_probe) {
+ Projection correction;
+ correction.columns[1][1] = -1.0;
+ projection = correction * render_data.cam_projection;
+ }
+
+ _draw_sky(render_data.environment, projection, render_data.cam_transform, sky_energy_multiplier, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post);
}
- if (scene_state.used_screen_texture || scene_state.used_depth_texture) {
+ if (rt && (scene_state.used_screen_texture || scene_state.used_depth_texture)) {
Size2i size;
GLuint backbuffer_fbo = 0;
GLuint backbuffer = 0;
@@ -2716,7 +2652,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glFrontFace(GL_CCW);
}
- if (rb.is_valid()) {
+ if (!is_reflection_probe && rb.is_valid()) {
_render_buffers_debug_draw(rb, p_shadow_atlas, fbo);
}
@@ -2724,9 +2660,11 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.reset_gl_state();
glUseProgram(0);
- _render_post_processing(&render_data);
+ if (!is_reflection_probe) {
+ _render_post_processing(&render_data);
- texture_storage->render_target_disable_clear_request(rb->render_target);
+ texture_storage->render_target_disable_clear_request(rb->render_target);
+ }
glActiveTexture(GL_TEXTURE0);
}
@@ -2770,6 +2708,34 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
rb->check_glow_buffers();
}
+ uint64_t bcs_spec_constants = 0;
+ if (p_render_data->environment.is_valid()) {
+ bool use_bcs = environment_get_adjustments_enabled(p_render_data->environment);
+ RID color_correction_texture = environment_get_color_correction(p_render_data->environment);
+ if (use_bcs) {
+ bcs_spec_constants |= PostShaderGLES3::USE_BCS;
+
+ if (color_correction_texture.is_valid()) {
+ bcs_spec_constants |= PostShaderGLES3::USE_COLOR_CORRECTION;
+
+ bool use_1d_lut = environment_get_use_1d_color_correction(p_render_data->environment);
+ GLenum texture_target = GL_TEXTURE_3D;
+ if (use_1d_lut) {
+ bcs_spec_constants |= PostShaderGLES3::USE_1D_LUT;
+ texture_target = GL_TEXTURE_2D;
+ }
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(texture_target, texture_storage->texture_get_texid(color_correction_texture));
+ glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(texture_target, GL_TEXTURE_MIN_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);
+ glTexParameteri(texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ }
+ }
+ }
+
if (view_count == 1) {
// Resolve if needed.
if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) {
@@ -2805,7 +2771,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
}
// Copy color buffer
- post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity);
+ post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, 0, false, bcs_spec_constants);
// Copy depth buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int);
@@ -2873,7 +2839,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
glBindFramebuffer(GL_FRAMEBUFFER, fbos[2]);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v);
- post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, v, true);
+ post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, v, true, bcs_spec_constants);
}
// Copy depth
@@ -2894,6 +2860,9 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt);
glDeleteFramebuffers(3, fbos);
}
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, 0);
}
template <PassMode p_pass_mode>
@@ -3079,6 +3048,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
} break;
+ case GLES3::SceneShaderData::BLEND_MODE_PREMULT_ALPHA: {
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ } break;
case GLES3::SceneShaderData::BLEND_MODE_ALPHA_TO_COVERAGE: {
// Do nothing for now.
} break;
@@ -3182,31 +3156,47 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
if (pass == 0) {
spec_constants |= SceneShaderGLES3::BASE_PASS;
- if (inst->omni_light_gl_cache.size() == 0) {
- spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
- }
- if (inst->spot_light_gl_cache.size() == 0) {
+ if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
- }
-
- if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) {
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
- }
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
+ } else {
+ if (inst->omni_light_gl_cache.size() == 0) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
+ }
- if (inst->lightmap_instance.is_valid()) {
- spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
+ if (inst->spot_light_gl_cache.size() == 0) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
+ }
- GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
- GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
+ if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
+ }
- if (lm->uses_spherical_harmonics) {
- spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
+ if (inst->reflection_probe_rid_cache.size() == 0) {
+ // We don't have any probes.
+ spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;
+ } else if (inst->reflection_probe_rid_cache.size() > 1) {
+ // We have a second probe.
+ spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
+ }
+
+ if (inst->lightmap_instance.is_valid()) {
+ spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
+
+ GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
+ GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
+
+ if (lm->uses_spherical_harmonics) {
+ spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
+ }
+ } else if (inst->lightmap_sh) {
+ spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
+ } else {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
}
- } else if (inst->lightmap_sh) {
- spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
- } else {
- spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
}
} else {
// Only base pass uses the radiance map.
@@ -3215,6 +3205,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
+ spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;
}
if (uses_additive_lighting) {
@@ -3374,6 +3365,52 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
}
+ // Pass in reflection probe data
+ if constexpr (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) {
+ if (pass == 0 && inst->reflection_probe_rid_cache.size() > 0) {
+ GLES3::Config *config = GLES3::Config::get_singleton();
+ GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton();
+
+ // Setup first probe.
+ {
+ RID probe_rid = light_storage->reflection_probe_instance_get_probe(inst->reflection_probe_rid_cache[0]);
+ GLES3::ReflectionProbe *probe = light_storage->get_reflection_probe(probe_rid);
+
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_USE_BOX_PROJECT, probe->box_projection, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_BOX_EXTENTS, probe->size * 0.5, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_BOX_OFFSET, probe->origin_offset, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_EXTERIOR, !probe->interior, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_INTENSITY, probe->intensity, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_AMBIENT_MODE, int(probe->ambient_mode), shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_AMBIENT_COLOR, probe->ambient_color * probe->ambient_color_energy, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_LOCAL_MATRIX, inst->reflection_probes_local_transform_cache[0], shader->version, instance_variant, spec_constants);
+
+ glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[0]));
+ }
+
+ if (inst->reflection_probe_rid_cache.size() > 1) {
+ // Setup second probe.
+ RID probe_rid = light_storage->reflection_probe_instance_get_probe(inst->reflection_probe_rid_cache[1]);
+ GLES3::ReflectionProbe *probe = light_storage->get_reflection_probe(probe_rid);
+
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_USE_BOX_PROJECT, probe->box_projection, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_BOX_EXTENTS, probe->size * 0.5, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_BOX_OFFSET, probe->origin_offset, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_EXTERIOR, !probe->interior, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_INTENSITY, probe->intensity, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_AMBIENT_MODE, int(probe->ambient_mode), shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_AMBIENT_COLOR, probe->ambient_color * probe->ambient_color_energy, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_LOCAL_MATRIX, inst->reflection_probes_local_transform_cache[1], shader->version, instance_variant, spec_constants);
+
+ glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 8);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[1]));
+
+ spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
+ }
+ }
+ }
+
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, spec_constants);
{
GLES3::Mesh::Surface *s = reinterpret_cast<GLES3::Mesh::Surface *>(surf->surface);
@@ -3559,12 +3596,12 @@ void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider,
scene_state.reset_gl_state();
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_LESS);
+ glDepthFunc(GL_GREATER);
glDrawBuffers(0, nullptr);
glColorMask(0, 0, 0, 0);
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
glClear(GL_DEPTH_BUFFER_BIT);
@@ -3605,7 +3642,7 @@ void RasterizerSceneGLES3::_render_uv2(const PagedArray<RenderGeometryInstance *
scene_state.reset_gl_state();
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_LESS);
+ glDepthFunc(GL_GREATER);
TightLocalVector<GLenum> draw_buffers;
draw_buffers.push_back(GL_COLOR_ATTACHMENT0);
@@ -3738,7 +3775,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
copy_effects->copy_cube_to_rect(atlas_uv_rect);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
} else {
glBindTexture(GL_TEXTURE_2D, shadow_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
@@ -3746,7 +3783,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
copy_effects->copy_to_rect(atlas_uv_rect);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
}
}
}
@@ -3782,7 +3819,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
@@ -4065,6 +4102,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() {
global_defines += "\n#define MAX_LIGHT_DATA_STRUCTS " + itos(config->max_renderable_lights) + "\n";
global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "u\n";
+ global_defines += "\n#define MAX_ROUGHNESS_LOD " + itos(sky_globals.roughness_layers - 1) + ".0\n";
material_storage->shaders.scene_shader.initialize(global_defines);
scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create();
material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR);
@@ -4120,7 +4158,6 @@ void fragment() {
{
// Initialize Sky stuff
sky_globals.roughness_layers = GLOBAL_GET("rendering/reflections/sky_reflections/roughness_layers");
- sky_globals.ggx_samples = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
String global_defines;
global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now
@@ -4130,13 +4167,6 @@ void fragment() {
}
{
- String global_defines;
- global_defines += "\n#define MAX_SAMPLE_COUNT " + itos(sky_globals.ggx_samples) + "\n";
- material_storage->shaders.cubemap_filter_shader.initialize(global_defines);
- scene_globals.cubemap_filter_shader_version = material_storage->shaders.cubemap_filter_shader.version_create();
- }
-
- {
sky_globals.default_shader = material_storage->shader_allocate();
material_storage->shader_initialize(sky_globals.default_shader);
@@ -4225,7 +4255,6 @@ RasterizerSceneGLES3::~RasterizerSceneGLES3() {
// Scene Shader
GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_free(scene_globals.shader_default_version);
- GLES3::MaterialStorage::get_singleton()->shaders.cubemap_filter_shader.version_free(scene_globals.cubemap_filter_shader_version);
RSG::material_storage->material_free(scene_globals.default_material);
RSG::material_storage->shader_free(scene_globals.default_shader);
@@ -4241,7 +4270,6 @@ RasterizerSceneGLES3::~RasterizerSceneGLES3() {
RSG::material_storage->shader_free(sky_globals.fog_shader);
GLES3::Utilities::get_singleton()->buffer_free_data(sky_globals.screen_triangle);
glDeleteVertexArrays(1, &sky_globals.screen_triangle_array);
- glDeleteTextures(1, &sky_globals.radical_inverse_vdc_cache_tex);
GLES3::Utilities::get_singleton()->buffer_free_data(sky_globals.directional_light_buffer);
memdelete_arr(sky_globals.directional_lights);
memdelete_arr(sky_globals.last_frame_directional_lights);
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index 71cd152520..c656ee3cc7 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -37,7 +37,7 @@
#include "core/templates/paged_allocator.h"
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
-#include "drivers/gles3/shaders/cubemap_filter.glsl.gen.h"
+#include "drivers/gles3/shaders/effects/cubemap_filter.glsl.gen.h"
#include "drivers/gles3/shaders/sky.glsl.gen.h"
#include "scene/resources/mesh.h"
#include "servers/rendering/renderer_compositor.h"
@@ -157,7 +157,6 @@ private:
RID shader_default_version;
RID default_material;
RID default_shader;
- RID cubemap_filter_shader_version;
RID overdraw_material;
RID overdraw_shader;
} scene_globals;
@@ -314,6 +313,10 @@ private:
LocalVector<uint32_t> omni_light_gl_cache;
LocalVector<uint32_t> spot_light_gl_cache;
+ LocalVector<RID> paired_reflection_probes;
+ LocalVector<RID> reflection_probe_rid_cache;
+ LocalVector<Transform3D> reflection_probes_local_transform_cache;
+
RID lightmap_instance;
Rect2 lightmap_uv_scale;
uint32_t lightmap_slice_index;
@@ -331,7 +334,7 @@ private:
virtual void set_lightmap_capture(const Color *p_sh9) override;
virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override;
- virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override {}
+ virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override;
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
@@ -431,6 +434,11 @@ private:
float white = 1.0;
int32_t tonemapper = 0;
int32_t pad = 0;
+
+ int32_t pad2 = 0;
+ float brightness = 1.0;
+ float contrast = 1.0;
+ float saturation = 1.0;
};
static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes");
@@ -686,10 +694,8 @@ protected:
RID fog_shader;
GLuint screen_triangle = 0;
GLuint screen_triangle_array = 0;
- GLuint radical_inverse_vdc_cache_tex = 0;
uint32_t max_directional_lights = 4;
uint32_t roughness_layers = 8;
- uint32_t ggx_samples = 128;
} sky_globals;
struct Sky {
@@ -733,7 +739,6 @@ protected:
void _invalidate_sky(Sky *p_sky);
void _update_dirty_skys();
void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier);
- void _filter_sky_radiance(Sky *p_sky, int p_base_layer);
void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier, float p_luminance_multiplier, bool p_use_multiview, bool p_flip_y, bool p_apply_color_adjustments_in_post);
void _free_sky_data(Sky *p_sky);
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub
index 0292b5d519..e70912cb4d 100644
--- a/drivers/gles3/shaders/SCsub
+++ b/drivers/gles3/shaders/SCsub
@@ -18,7 +18,6 @@ if "GLES3_GLSL" in env["BUILDERS"]:
env.GLES3_GLSL("canvas.glsl")
env.GLES3_GLSL("scene.glsl")
env.GLES3_GLSL("sky.glsl")
- env.GLES3_GLSL("cubemap_filter.glsl")
env.GLES3_GLSL("canvas_occlusion.glsl")
env.GLES3_GLSL("canvas_sdf.glsl")
env.GLES3_GLSL("particles.glsl")
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 8da7d7dc80..65332c06be 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -160,15 +160,18 @@ void main() {
if (gl_VertexID % 3 == 0) {
vertex = read_draw_data_point_a;
uv = read_draw_data_uv_a;
- color = vec4(unpackHalf2x16(read_draw_data_color_a_rg), unpackHalf2x16(read_draw_data_color_a_ba));
+ color.xy = unpackHalf2x16(read_draw_data_color_a_rg);
+ color.zw = unpackHalf2x16(read_draw_data_color_a_ba);
} else if (gl_VertexID % 3 == 1) {
vertex = read_draw_data_point_b;
uv = read_draw_data_uv_b;
- color = vec4(unpackHalf2x16(read_draw_data_color_b_rg), unpackHalf2x16(read_draw_data_color_b_ba));
+ color.xy = unpackHalf2x16(read_draw_data_color_b_rg);
+ color.zw = unpackHalf2x16(read_draw_data_color_b_ba);
} else {
vertex = read_draw_data_point_c;
uv = read_draw_data_uv_c;
- color = vec4(unpackHalf2x16(read_draw_data_color_c_rg), unpackHalf2x16(read_draw_data_color_c_ba));
+ color.xy = unpackHalf2x16(read_draw_data_color_c_rg);
+ color.zw = unpackHalf2x16(read_draw_data_color_c_ba);
}
#elif defined(USE_ATTRIBUTES)
@@ -178,11 +181,14 @@ void main() {
#ifdef USE_INSTANCING
if (bool(read_draw_data_flags & FLAGS_INSTANCING_HAS_COLORS)) {
- vec4 instance_color = vec4(unpackHalf2x16(instance_color_custom_data.x), unpackHalf2x16(instance_color_custom_data.y));
+ vec4 instance_color;
+ instance_color.xy = unpackHalf2x16(uint(instance_color_custom_data.x));
+ instance_color.zw = unpackHalf2x16(uint(instance_color_custom_data.y));
color *= instance_color;
}
if (bool(read_draw_data_flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
- instance_custom = vec4(unpackHalf2x16(instance_color_custom_data.z), unpackHalf2x16(instance_color_custom_data.w));
+ instance_custom.xy = unpackHalf2x16(instance_color_custom_data.z);
+ instance_custom.zw = unpackHalf2x16(instance_color_custom_data.w);
}
#endif // !USE_INSTANCING
@@ -770,6 +776,12 @@ void main() {
vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array[light_base].texture_matrix[0], light_array[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 tex_uv_atlas = tex_uv * light_array[light_base].atlas_rect.zw + light_array[light_base].atlas_rect.xy;
+
+ if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
+ //if outside the light texture, light color is zero
+ continue;
+ }
+
vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0);
vec4 light_base_color = light_array[light_base].color;
@@ -794,10 +806,6 @@ void main() {
light_color.rgb *= base_color.rgb;
}
#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_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
diff --git a/drivers/gles3/shaders/cube_to_dp.glsl b/drivers/gles3/shaders/cube_to_dp.glsl
index 2384529a89..ec1982738a 100644
--- a/drivers/gles3/shaders/cube_to_dp.glsl
+++ b/drivers/gles3/shaders/cube_to_dp.glsl
@@ -95,6 +95,6 @@ void main() {
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;
+ float linear_depth = 2.0 * z_near * z_far / (z_far + z_near + depth * (z_far - z_near));
+ gl_FragDepth = (z_far - (linear_depth * depth_fix + bias)) / z_far;
}
diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/effects/cubemap_filter.glsl
index 6fcb23204d..6fcb23204d 100644
--- a/drivers/gles3/shaders/cubemap_filter.glsl
+++ b/drivers/gles3/shaders/effects/cubemap_filter.glsl
diff --git a/drivers/gles3/shaders/effects/post.glsl b/drivers/gles3/shaders/effects/post.glsl
index e61171c92a..904158abcd 100644
--- a/drivers/gles3/shaders/effects/post.glsl
+++ b/drivers/gles3/shaders/effects/post.glsl
@@ -1,13 +1,15 @@
/* clang-format off */
#[modes]
-mode_default = #define MODE_DEFAULT
-// mode_glow = #define MODE_GLOW
+mode_default =
#[specializations]
USE_MULTIVIEW = false
USE_GLOW = false
USE_LUMINANCE_MULTIPLIER = false
+USE_BCS = false
+USE_COLOR_CORRECTION = false
+USE_1D_LUT = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
@@ -25,6 +27,9 @@ void main() {
#[fragment]
/* clang-format on */
+// If we reach this code, we always tonemap.
+#define APPLY_TONEMAPPING
+
#include "../tonemap_inc.glsl"
#ifdef USE_MULTIVIEW
@@ -57,6 +62,35 @@ vec4 get_glow_color(vec2 uv) {
}
#endif // USE_GLOW
+#ifdef USE_COLOR_CORRECTION
+#ifdef USE_1D_LUT
+uniform sampler2D source_color_correction; //texunit:2
+
+vec3 apply_color_correction(vec3 color) {
+ color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r;
+ color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g;
+ color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b;
+ return color;
+}
+#else
+uniform sampler3D source_color_correction; //texunit:2
+
+vec3 apply_color_correction(vec3 color) {
+ return textureLod(source_color_correction, color, 0.0).rgb;
+}
+#endif // USE_1D_LUT
+#endif // USE_COLOR_CORRECTION
+
+#ifdef USE_BCS
+vec3 apply_bcs(vec3 color) {
+ color = mix(vec3(0.0), color, brightness);
+ color = mix(vec3(0.5), color, contrast);
+ color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, saturation);
+
+ return color;
+}
+#endif
+
in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
@@ -85,11 +119,11 @@ void main() {
color.rgb = linear_to_srgb(color.rgb);
#ifdef USE_BCS
- color.rgb = apply_bcs(color.rgb, bcs);
+ color.rgb = apply_bcs(color.rgb);
#endif
#ifdef USE_COLOR_CORRECTION
- color.rgb = apply_color_correction(color.rgb, color_correction);
+ color.rgb = apply_color_correction(color.rgb);
#endif
frag_color = color;
diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl
index d95f7f4309..096f0a57ae 100644
--- a/drivers/gles3/shaders/particles.glsl
+++ b/drivers/gles3/shaders/particles.glsl
@@ -339,7 +339,8 @@ void main() {
amount = max(0.0, 1.0 - d);
} else if (attractors[i].type == ATTRACTOR_TYPE_VECTOR_FIELD) {
}
- amount = pow(amount, attractors[i].attenuation);
+ mediump float attractor_attenuation = attractors[i].attenuation;
+ amount = pow(amount, attractor_attenuation);
dir = safe_normalize(mix(dir, attractors[i].transform[2].xyz, attractors[i].directionality));
attractor_force -= amount * dir * attractors[i].strength;
}
diff --git a/drivers/gles3/shaders/particles_copy.glsl b/drivers/gles3/shaders/particles_copy.glsl
index 0bb8efc52d..55b5e6d7ce 100644
--- a/drivers/gles3/shaders/particles_copy.glsl
+++ b/drivers/gles3/shaders/particles_copy.glsl
@@ -57,45 +57,39 @@ void main() {
txform = transpose(mat4(xform_1, xform_2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)));
#endif
- switch (align_mode) {
- case TRANSFORM_ALIGN_DISABLED: {
- } break; //nothing
- case TRANSFORM_ALIGN_Z_BILLBOARD: {
- mat3 local = mat3(normalize(cross(align_up, sort_direction)), align_up, sort_direction);
- local = local * mat3(txform);
- txform[0].xyz = local[0];
- txform[1].xyz = local[1];
- txform[2].xyz = local[2];
-
- } break;
- case TRANSFORM_ALIGN_Y_TO_VELOCITY: {
- vec3 v = velocity_flags.xyz;
- float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0;
- if (length(v) > 0.0) {
- txform[1].xyz = normalize(v);
- } else {
- txform[1].xyz = normalize(txform[1].xyz);
- }
-
- txform[0].xyz = normalize(cross(txform[1].xyz, txform[2].xyz));
- txform[2].xyz = vec3(0.0, 0.0, 1.0) * s;
- txform[0].xyz *= s;
- txform[1].xyz *= s;
- } break;
- case TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY: {
- vec3 sv = velocity_flags.xyz - sort_direction * dot(sort_direction, velocity_flags.xyz); //screen velocity
-
- if (length(sv) == 0.0) {
- sv = align_up;
- }
-
- sv = normalize(sv);
-
- txform[0].xyz = normalize(cross(sv, sort_direction)) * length(txform[0]);
- txform[1].xyz = sv * length(txform[1]);
- txform[2].xyz = sort_direction * length(txform[2]);
-
- } break;
+ if (align_mode == TRANSFORM_ALIGN_DISABLED) {
+ // nothing
+ } else if (align_mode == TRANSFORM_ALIGN_Z_BILLBOARD) {
+ mat3 local = mat3(normalize(cross(align_up, sort_direction)), align_up, sort_direction);
+ local = local * mat3(txform);
+ txform[0].xyz = local[0];
+ txform[1].xyz = local[1];
+ txform[2].xyz = local[2];
+ } else if (align_mode == TRANSFORM_ALIGN_Y_TO_VELOCITY) {
+ vec3 v = velocity_flags.xyz;
+ float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0;
+ if (length(v) > 0.0) {
+ txform[1].xyz = normalize(v);
+ } else {
+ txform[1].xyz = normalize(txform[1].xyz);
+ }
+
+ txform[0].xyz = normalize(cross(txform[1].xyz, txform[2].xyz));
+ txform[2].xyz = vec3(0.0, 0.0, 1.0) * s;
+ txform[0].xyz *= s;
+ txform[1].xyz *= s;
+ } else if (align_mode == TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
+ vec3 sv = velocity_flags.xyz - sort_direction * dot(sort_direction, velocity_flags.xyz); //screen velocity
+
+ if (length(sv) == 0.0) {
+ sv = align_up;
+ }
+
+ sv = normalize(sv);
+
+ txform[0].xyz = normalize(cross(sv, sort_direction)) * length(txform[0]);
+ txform[1].xyz = sv * length(txform[1]);
+ txform[2].xyz = sort_direction * length(txform[2]);
}
txform[3].xyz += velocity_flags.xyz * frame_remainder;
@@ -108,7 +102,10 @@ void main() {
}
txform = transpose(txform);
- instance_color_custom_data = uvec4(packHalf2x16(color.xy), packHalf2x16(color.zw), packHalf2x16(custom.xy), packHalf2x16(custom.zw));
+ instance_color_custom_data.x = packHalf2x16(color.xy);
+ instance_color_custom_data.y = packHalf2x16(color.zw);
+ instance_color_custom_data.z = packHalf2x16(custom.xy);
+ instance_color_custom_data.w = packHalf2x16(custom.zw);
out_xform_1 = txform[0];
out_xform_2 = txform[1];
#ifdef MODE_3D
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index d73407d674..2b372cb88d 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -12,6 +12,7 @@ DISABLE_LIGHTMAP = false
DISABLE_LIGHT_DIRECTIONAL = false
DISABLE_LIGHT_OMNI = false
DISABLE_LIGHT_SPOT = false
+DISABLE_REFLECTION_PROBE = true
DISABLE_FOG = false
USE_DEPTH_FOG = false
USE_RADIANCE_MAP = true
@@ -34,6 +35,7 @@ APPLY_TONEMAPPING = true
ADDITIVE_OMNI = false
ADDITIVE_SPOT = false
RENDER_MATERIAL = false
+SECOND_REFLECTION_PROBE = false
#[vertex]
@@ -366,7 +368,9 @@ void main() {
#if defined(COLOR_USED)
color_interp = color_attrib;
#ifdef USE_INSTANCING
- vec4 instance_color = vec4(unpackHalf2x16(instance_color_custom_data.x), unpackHalf2x16(instance_color_custom_data.y));
+ vec4 instance_color;
+ instance_color.xy = unpackHalf2x16(instance_color_custom_data.x);
+ instance_color.zw = unpackHalf2x16(instance_color_custom_data.y);
color_interp *= instance_color;
#endif
#endif
@@ -403,7 +407,9 @@ void main() {
#endif //USE_MULTIVIEW
#ifdef USE_INSTANCING
- vec4 instance_custom = vec4(unpackHalf2x16(instance_color_custom_data.z), unpackHalf2x16(instance_color_custom_data.w));
+ vec4 instance_custom;
+ instance_custom.xy = unpackHalf2x16(instance_color_custom_data.z);
+ instance_custom.zw = unpackHalf2x16(instance_color_custom_data.w);
#else
vec4 instance_custom = vec4(0.0);
#endif
@@ -564,8 +570,11 @@ void main() {
1-color correction // In tonemap_inc.glsl
2-radiance
3-shadow
+4-lightmap textures
5-screen
6-depth
+7-reflection probe 1
+8-reflection probe 2
*/
@@ -622,7 +631,39 @@ in highp vec4 shadow_coord4;
uniform samplerCube radiance_map; // texunit:-2
-#endif
+#endif // USE_RADIANCE_MAP
+
+#ifndef DISABLE_REFLECTION_PROBE
+
+#define REFLECTION_PROBE_MAX_LOD 8.0
+
+uniform bool refprobe1_use_box_project;
+uniform highp vec3 refprobe1_box_extents;
+uniform vec3 refprobe1_box_offset;
+uniform highp mat4 refprobe1_local_matrix;
+uniform bool refprobe1_exterior;
+uniform float refprobe1_intensity;
+uniform int refprobe1_ambient_mode;
+uniform vec4 refprobe1_ambient_color;
+
+uniform samplerCube refprobe1_texture; // texunit:-7
+
+#ifdef SECOND_REFLECTION_PROBE
+
+uniform bool refprobe2_use_box_project;
+uniform highp vec3 refprobe2_box_extents;
+uniform vec3 refprobe2_box_offset;
+uniform highp mat4 refprobe2_local_matrix;
+uniform bool refprobe2_exterior;
+uniform float refprobe2_intensity;
+uniform int refprobe2_ambient_mode;
+uniform vec4 refprobe2_ambient_color;
+
+uniform samplerCube refprobe2_texture; // texunit:-8
+
+#endif // SECOND_REFLECTION_PROBE
+
+#endif // DISABLE_REFLECTION_PROBE
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
@@ -1285,6 +1326,90 @@ vec4 fog_process(vec3 vertex) {
return vec4(fog_color, fog_amount);
}
+#ifndef DISABLE_REFLECTION_PROBE
+
+#define REFLECTION_AMBIENT_DISABLED 0
+#define REFLECTION_AMBIENT_ENVIRONMENT 1
+#define REFLECTION_AMBIENT_COLOR 2
+
+void reflection_process(samplerCube reflection_map,
+ vec3 normal, vec3 vertex,
+ mat4 local_matrix,
+ bool use_box_project, vec3 box_extents, vec3 box_offset,
+ bool exterior, float intensity, int ref_ambient_mode, vec4 ref_ambient_color,
+ float roughness, vec3 ambient, vec3 skybox,
+ inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) {
+ vec4 reflection;
+
+ 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 = srgb_to_linear(textureLod(reflection_map, ref_normal, roughness * MAX_ROUGHNESS_LOD).rgb);
+
+ if (exterior) {
+ reflection.rgb = mix(skybox, reflection.rgb, blend);
+ }
+ reflection.rgb *= intensity;
+ reflection.a = blend;
+ reflection.rgb *= blend;
+
+ reflection_accum += reflection;
+
+#ifndef USE_LIGHTMAP
+ if (ref_ambient_mode == REFLECTION_AMBIENT_ENVIRONMENT) {
+ vec4 ambient_out;
+ vec3 amb_normal = (local_matrix * vec4(normal, 0.0)).xyz;
+
+ ambient_out.rgb = srgb_to_linear(textureLod(reflection_map, amb_normal, MAX_ROUGHNESS_LOD).rgb);
+ if (exterior) {
+ ambient_out.rgb = mix(ambient, ambient_out.rgb, blend);
+ }
+
+ ambient_out.a = blend;
+ ambient_out.rgb *= blend;
+ ambient_accum += ambient_out;
+ } else if (ref_ambient_mode == REFLECTION_AMBIENT_COLOR) {
+ vec4 ambient_out;
+ ambient_out.rgb = ref_ambient_color.rgb;
+ if (exterior) {
+ ambient_out.rgb = mix(ambient, ambient_out.rgb, blend);
+ }
+
+ ambient_out.a = blend;
+ ambient_out.rgb *= blend;
+ ambient_accum += ambient_out;
+ }
+#endif // USE_LIGHTMAP
+}
+
+#endif // DISABLE_REFLECTION_PROBE
+
#endif // !MODE_RENDER_DEPTH
void main() {
@@ -1317,6 +1442,9 @@ void main() {
float clearcoat_roughness = 0.0;
float anisotropy = 0.0;
vec2 anisotropy_flow = vec2(1.0, 0.0);
+#ifdef PREMUL_ALPHA_USED
+ float premul_alpha = 1.0;
+#endif // PREMUL_ALPHA_USED
#ifndef FOG_DISABLED
vec4 fog = vec4(0.0);
#endif // !FOG_DISABLED
@@ -1386,16 +1514,31 @@ void main() {
float alpha_antialiasing_edge = 0.0;
vec2 alpha_texture_coordinate = vec2(0.0, 0.0);
#endif // ALPHA_ANTIALIASING_EDGE_USED
+
+#ifdef LIGHT_VERTEX_USED
+ vec3 light_vertex = vertex;
+#endif //LIGHT_VERTEX_USED
+
{
#CODE : FRAGMENT
}
+#ifdef LIGHT_VERTEX_USED
+ vertex = light_vertex;
+#ifdef USE_MULTIVIEW
+ view = -normalize(vertex - eye_offset);
+#else
+ view = -normalize(vertex);
+#endif //USE_MULTIVIEW
+#endif //LIGHT_VERTEX_USED
+
#ifndef USE_SHADOW_TO_OPACITY
#if defined(ALPHA_SCISSOR_USED)
if (alpha < alpha_scissor_threshold) {
discard;
}
+ alpha = 1.0;
#else
#ifdef MODE_RENDER_DEPTH
#ifdef USE_OPAQUE_PREPASS
@@ -1485,9 +1628,33 @@ void main() {
specular_light *= horizon * horizon;
specular_light *= scene_data.ambient_light_color_energy.a;
}
-#endif
+#endif // USE_RADIANCE_MAP
// Calculate Reflection probes
+#ifndef DISABLE_REFLECTION_PROBE
+ vec4 ambient_accum = vec4(0.0);
+ {
+ vec4 reflection_accum = vec4(0.0);
+
+ reflection_process(refprobe1_texture, normal, vertex_interp, refprobe1_local_matrix,
+ refprobe1_use_box_project, refprobe1_box_extents, refprobe1_box_offset,
+ refprobe1_exterior, refprobe1_intensity, refprobe1_ambient_mode, refprobe1_ambient_color,
+ roughness, ambient_light, specular_light, reflection_accum, ambient_accum);
+
+#ifdef SECOND_REFLECTION_PROBE
+
+ reflection_process(refprobe2_texture, normal, vertex_interp, refprobe2_local_matrix,
+ refprobe2_use_box_project, refprobe2_box_extents, refprobe2_box_offset,
+ refprobe2_exterior, refprobe2_intensity, refprobe2_ambient_mode, refprobe2_ambient_color,
+ roughness, ambient_light, specular_light, reflection_accum, ambient_accum);
+
+#endif // SECOND_REFLECTION_PROBE
+
+ if (reflection_accum.a > 0.0) {
+ specular_light = reflection_accum.rgb / reflection_accum.a;
+ }
+ }
+#endif // DISABLE_REFLECTION_PROBE
#if defined(CUSTOM_RADIANCE_USED)
specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a);
@@ -1497,6 +1664,7 @@ void main() {
//lightmap overrides everything
if (scene_data.use_ambient_light) {
ambient_light = scene_data.ambient_light_color_energy.rgb;
+
#ifdef USE_RADIANCE_MAP
if (scene_data.use_ambient_cubemap) {
vec3 ambient_dir = scene_data.radiance_inverse_xform * normal;
@@ -1504,7 +1672,13 @@ void main() {
cubemap_ambient = srgb_to_linear(cubemap_ambient);
ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
}
-#endif
+#endif // USE_RADIANCE_MAP
+
+#ifndef DISABLE_REFLECTION_PROBE
+ if (ambient_accum.a > 0.0) {
+ ambient_light = mix(ambient_light, (ambient_accum.rgb / ambient_accum.a) * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
+ }
+#endif // DISABLE_REFLECTION_PROBE
}
#endif // USE_LIGHTMAP
@@ -1688,29 +1862,22 @@ void main() {
#endif // !MODE_RENDER_DEPTH
#if defined(USE_SHADOW_TO_OPACITY)
+#ifndef MODE_RENDER_DEPTH
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
#if defined(ALPHA_SCISSOR_USED)
if (alpha < alpha_scissor) {
discard;
}
-#else
-#ifdef MODE_RENDER_DEPTH
-#ifdef USE_OPAQUE_PREPASS
-
- if (alpha < opaque_prepass_threshold) {
- discard;
- }
-#endif // USE_OPAQUE_PREPASS
-#endif // MODE_RENDER_DEPTH
#endif // !ALPHA_SCISSOR_USED
+#endif // !MODE_RENDER_DEPTH
#endif // USE_SHADOW_TO_OPACITY
#ifdef MODE_RENDER_DEPTH
#ifdef RENDER_SHADOWS_LINEAR
// Linearize the depth buffer if rendering cubemap shadows.
- gl_FragDepth = (length(vertex) + scene_data.shadow_bias) / scene_data.z_far;
+ gl_FragDepth = (scene_data.z_far - (length(vertex) + scene_data.shadow_bias)) / scene_data.z_far;
#endif
// Nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
@@ -1747,7 +1914,8 @@ void main() {
#endif //!MODE_UNSHADED
#ifndef FOG_DISABLED
- fog = vec4(unpackHalf2x16(fog_rg), unpackHalf2x16(fog_ba));
+ fog.xy = unpackHalf2x16(fog_rg);
+ fog.zw = unpackHalf2x16(fog_ba);
#ifndef DISABLE_FOG
if (scene_data.fog_enabled) {
@@ -1763,13 +1931,6 @@ void main() {
#endif
frag_color.rgb = linear_to_srgb(frag_color.rgb);
-#ifdef USE_BCS
- frag_color.rgb = apply_bcs(frag_color.rgb, bcs);
-#endif
-
-#ifdef USE_COLOR_CORRECTION
- frag_color.rgb = apply_color_correction(frag_color.rgb, color_correction);
-#endif
#else // !BASE_PASS
frag_color = vec4(0.0, 0.0, 0.0, alpha);
#endif // !BASE_PASS
@@ -1914,7 +2075,7 @@ void main() {
float omni_shadow = 1.0f;
#ifndef SHADOWS_DISABLED
vec3 light_ray = ((positional_shadows[positional_shadow_index].shadow_matrix * vec4(shadow_coord.xyz, 1.0))).xyz;
- omni_shadow = texture(omni_shadow_texture, vec4(light_ray, length(light_ray) * omni_lights[omni_light_index].inv_radius));
+ omni_shadow = texture(omni_shadow_texture, vec4(light_ray, 1.0 - length(light_ray) * omni_lights[omni_light_index].inv_radius));
omni_shadow = mix(1.0, omni_shadow, omni_lights[omni_light_index].shadow_opacity);
#endif // SHADOWS_DISABLED
light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha,
@@ -1964,7 +2125,8 @@ void main() {
vec3 additive_light_color = diffuse_light + specular_light;
#ifndef FOG_DISABLED
- fog = vec4(unpackHalf2x16(fog_rg), unpackHalf2x16(fog_ba));
+ fog.xy = unpackHalf2x16(fog_rg);
+ fog.zw = unpackHalf2x16(fog_ba);
#ifndef DISABLE_FOG
if (scene_data.fog_enabled) {
@@ -1980,19 +2142,14 @@ void main() {
#endif
additive_light_color = linear_to_srgb(additive_light_color);
-#ifdef USE_BCS
- additive_light_color = apply_bcs(additive_light_color, bcs);
-#endif
-
-#ifdef USE_COLOR_CORRECTION
- additive_light_color = apply_color_correction(additive_light_color, color_correction);
-#endif
-
frag_color.rgb += additive_light_color;
#endif // USE_ADDITIVE_LIGHTING
-
frag_color.rgb *= scene_data.luminance_multiplier;
#endif // !RENDER_MATERIAL
#endif // !MODE_RENDER_DEPTH
+
+#ifdef PREMUL_ALPHA_USED
+ frag_color.rgb *= premul_alpha;
+#endif // PREMUL_ALPHA_USED
}
diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl
index b10ea12e6e..26549901a6 100644
--- a/drivers/gles3/shaders/sky.glsl
+++ b/drivers/gles3/shaders/sky.glsl
@@ -28,7 +28,7 @@ void main() {
// We're doing clockwise culling so flip the order
uv_interp = vec2(vertex_attrib.x, vertex_attrib.y * -1.0);
#endif
- gl_Position = vec4(uv_interp, 1.0, 1.0);
+ gl_Position = vec4(uv_interp, -1.0, 1.0);
}
/* clang-format off */
@@ -139,9 +139,11 @@ void main() {
vec3 cube_normal;
#ifdef USE_MULTIVIEW
// In multiview our projection matrices will contain positional and rotational offsets that we need to properly unproject.
- vec4 unproject = vec4(uv_interp.x, uv_interp.y, 1.0, 1.0);
+ vec4 unproject = vec4(uv_interp.xy, -1.0, 1.0); // unproject at the far plane
vec4 unprojected = multiview_data.inv_projection_matrix_view[ViewIndex] * unproject;
cube_normal = unprojected.xyz / unprojected.w;
+
+ // Unproject will give us the position between the eyes, need to re-offset.
cube_normal += multiview_data.eye_offset[ViewIndex].xyz;
#else
cube_normal.z = -1.0;
@@ -207,14 +209,6 @@ void main() {
#endif
color = linear_to_srgb(color);
-#ifdef USE_BCS
- color = apply_bcs(color, bcs);
-#endif
-
-#ifdef USE_COLOR_CORRECTION
- color = apply_color_correction(color, color_correction);
-#endif
-
frag_color.rgb = color * luminance_multiplier;
frag_color.a = alpha;
diff --git a/drivers/gles3/shaders/stdlib_inc.glsl b/drivers/gles3/shaders/stdlib_inc.glsl
index 92bf2d87e4..029084c34c 100644
--- a/drivers/gles3/shaders/stdlib_inc.glsl
+++ b/drivers/gles3/shaders/stdlib_inc.glsl
@@ -1,5 +1,12 @@
-#ifdef USE_GLES_OVER_GL
+// Compatibility renames. These are exposed with the "godot_" prefix
+// to work around two distinct Adreno bugs:
+// 1. Some Adreno devices expose ES310 functions in ES300 shaders.
+// Internally, we must use the "godot_" prefix, but user shaders
+// will be mapped automatically.
+// 2. Adreno 3XX devices have poor implementations of the other packing
+// functions, so we just use our own everywhere to keep it simple.
+
// Floating point pack/unpack functions are part of the GLSL ES 300 specification used by web and mobile.
uint float2half(uint f) {
uint e = f & uint(0x7f800000);
@@ -17,40 +24,34 @@ uint half2float(uint h) {
return ((h & uint(0x8000)) << uint(16)) | uint((h_e >> uint(10)) != uint(0)) * (((h_e + uint(0x1c000)) << uint(13)) | ((h & uint(0x03ff)) << uint(13)));
}
-uint packHalf2x16(vec2 v) {
+uint godot_packHalf2x16(vec2 v) {
return float2half(floatBitsToUint(v.x)) | float2half(floatBitsToUint(v.y)) << uint(16);
}
-vec2 unpackHalf2x16(uint v) {
+vec2 godot_unpackHalf2x16(uint v) {
return vec2(uintBitsToFloat(half2float(v & uint(0xffff))),
uintBitsToFloat(half2float(v >> uint(16))));
}
-uint packUnorm2x16(vec2 v) {
+uint godot_packUnorm2x16(vec2 v) {
uvec2 uv = uvec2(round(clamp(v, vec2(0.0), vec2(1.0)) * 65535.0));
return uv.x | uv.y << uint(16);
}
-vec2 unpackUnorm2x16(uint p) {
+vec2 godot_unpackUnorm2x16(uint p) {
return vec2(float(p & uint(0xffff)), float(p >> uint(16))) * 0.000015259021; // 1.0 / 65535.0 optimization
}
-uint packSnorm2x16(vec2 v) {
+uint godot_packSnorm2x16(vec2 v) {
uvec2 uv = uvec2(round(clamp(v, vec2(-1.0), vec2(1.0)) * 32767.0) + 32767.0);
return uv.x | uv.y << uint(16);
}
-vec2 unpackSnorm2x16(uint p) {
+vec2 godot_unpackSnorm2x16(uint p) {
vec2 v = vec2(float(p & uint(0xffff)), float(p >> uint(16)));
return clamp((v - 32767.0) * vec2(0.00003051851), vec2(-1.0), vec2(1.0));
}
-#endif
-
-// Compatibility renames. These are exposed with the "godot_" prefix
-// to work around an Adreno bug which was exposing these ES310 functions
-// in ES300 shaders. Internally, we must use the "godot_" prefix, but user shaders
-// will be mapped automatically.
uint godot_packUnorm4x8(vec4 v) {
uvec4 uv = uvec4(round(clamp(v, vec4(0.0), vec4(1.0)) * 255.0));
return uv.x | (uv.y << uint(8)) | (uv.z << uint(16)) | (uv.w << uint(24));
@@ -74,3 +75,9 @@ vec4 godot_unpackSnorm4x8(uint p) {
#define unpackUnorm4x8 godot_unpackUnorm4x8
#define packSnorm4x8 godot_packSnorm4x8
#define unpackSnorm4x8 godot_unpackSnorm4x8
+#define packHalf2x16 godot_packHalf2x16
+#define unpackHalf2x16 godot_unpackHalf2x16
+#define packUnorm2x16 godot_packUnorm2x16
+#define unpackUnorm2x16 godot_unpackUnorm2x16
+#define packSnorm2x16 godot_packSnorm2x16
+#define unpackSnorm2x16 godot_unpackSnorm2x16
diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl
deleted file mode 100644
index 0b769e77f2..0000000000
--- a/drivers/gles3/shaders/tonemap.glsl
+++ /dev/null
@@ -1,333 +0,0 @@
-/* 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;
-
-#ifdef USE_MULTIVIEW
-uniform highp sampler2DArray source; //texunit:0
-#else
-uniform highp sampler2D source; //texunit:0
-#endif
-
-#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;
-
-#ifdef USE_MULTIVIEW
- vec3 rgbNW = textureLod(source, vec3(uv_interp + vec2(-1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz;
- vec3 rgbNE = textureLod(source, vec3(uv_interp + vec2(1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz;
- vec3 rgbSW = textureLod(source, vec3(uv_interp + vec2(-1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz;
- vec3 rgbSE = textureLod(source, vec3(uv_interp + vec2(1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz;
-#else
- 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;
-#endif
- 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;
-
-#ifdef USE_MULTIVIEW
- vec3 rgbA = 0.5 * (textureLod(source, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz);
- vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz);
-#else
- 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);
-#endif
-
- float lumaB = dot(rgbB, luma);
- if ((lumaB < lumaMin) || (lumaB > lumaMax)) {
- return rgbA;
- } else {
- return rgbB;
- }
-}
-
-void main() {
-#ifdef USE_MULTIVIEW
- vec4 color = textureLod(source, vec3(uv_interp, ViewIndex), 0.0);
-#else
- vec4 color = textureLod(source, uv_interp, 0.0);
-#endif
-
-#ifdef USE_FXAA
- color.rgb = apply_fxaa(color.rgb, 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.rgb = apply_glow(color.rgb, glow);
-#endif
-
- // Additional effects
-
-#ifdef USE_BCS
- color.rgb = apply_bcs(color.rgb, bcs);
-#endif
-
-#ifdef USE_COLOR_CORRECTION
- color.rgb = apply_color_correction(color.rgb, color_correction);
-#endif
-
- frag_color = color;
-}
diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl
index f8f12760ec..fb915aeb38 100644
--- a/drivers/gles3/shaders/tonemap_inc.glsl
+++ b/drivers/gles3/shaders/tonemap_inc.glsl
@@ -1,43 +1,31 @@
-#ifdef USE_BCS
-uniform vec3 bcs;
-#endif
-
-#ifdef USE_COLOR_CORRECTION
-#ifdef USE_1D_LUT
-uniform sampler2D source_color_correction; //texunit:-1
-#else
-uniform sampler3D source_color_correction; //texunit:-1
-#endif
-#endif
-
layout(std140) uniform TonemapData { //ubo:0
float exposure;
float white;
int tonemapper;
int pad;
-};
-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);
+ int pad2;
+ float brightness;
+ float contrast;
+ float saturation;
+};
- return color;
-}
-#ifdef USE_COLOR_CORRECTION
-#ifdef USE_1D_LUT
-vec3 apply_color_correction(vec3 color) {
- color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r;
- color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g;
- color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b;
- return color;
+// This expects 0-1 range input.
+vec3 linear_to_srgb(vec3 color) {
+ //color = clamp(color, vec3(0.0), vec3(1.0));
+ //const vec3 a = vec3(0.055f);
+ //return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+ // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
+ return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0));
}
-#else
-vec3 apply_color_correction(vec3 color) {
- return textureLod(source_color_correction, color, 0.0).rgb;
+
+// This expects 0-1 range input, outside that range it behaves poorly.
+vec3 srgb_to_linear(vec3 color) {
+ // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
+ return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878);
}
-#endif
-#endif
+
+#ifdef APPLY_TONEMAPPING
vec3 tonemap_filmic(vec3 color, float p_white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
@@ -92,21 +80,6 @@ vec3 tonemap_reinhard(vec3 color, float p_white) {
return (p_white * color + color) / (color * p_white + p_white);
}
-// This expects 0-1 range input.
-vec3 linear_to_srgb(vec3 color) {
- //color = clamp(color, vec3(0.0), vec3(1.0));
- //const vec3 a = vec3(0.055f);
- //return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
- // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
- return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0));
-}
-
-// This expects 0-1 range input, outside that range it behaves poorly.
-vec3 srgb_to_linear(vec3 color) {
- // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
- return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878);
-}
-
#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
@@ -125,3 +98,5 @@ vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always
return tonemap_aces(max(vec3(0.0f), color), p_white);
}
}
+
+#endif // APPLY_TONEMAPPING
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp
index 1a41b60836..6b5e227782 100644
--- a/drivers/gles3/storage/config.cpp
+++ b/drivers/gles3/storage/config.cpp
@@ -157,7 +157,7 @@ Config::Config() {
continue;
}
- if (renderer.findn(v) != -1) {
+ if (renderer.containsn(v)) {
use_depth_prepass = false;
}
}
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index f5d1f8dabd..f9547502f4 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -423,149 +423,604 @@ void LightStorage::light_instance_mark_visible(RID p_light_instance) {
/* PROBE API */
RID LightStorage::reflection_probe_allocate() {
- return RID();
+ return reflection_probe_owner.allocate_rid();
}
void LightStorage::reflection_probe_initialize(RID p_rid) {
+ ReflectionProbe probe;
+
+ reflection_probe_owner.initialize_rid(p_rid, probe);
}
void LightStorage::reflection_probe_free(RID p_rid) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_rid);
+ reflection_probe->dependency.deleted_notify(p_rid);
+
+ reflection_probe_owner.free(p_rid);
}
void LightStorage::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->update_mode = p_mode;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
void LightStorage::reflection_probe_set_intensity(RID p_probe, float p_intensity) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->intensity = p_intensity;
}
void LightStorage::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->ambient_mode = p_mode;
}
void LightStorage::reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->ambient_color = p_color;
}
void LightStorage::reflection_probe_set_ambient_energy(RID p_probe, float p_energy) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->ambient_color_energy = p_energy;
}
void LightStorage::reflection_probe_set_max_distance(RID p_probe, float p_distance) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->max_distance = p_distance;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
void LightStorage::reflection_probe_set_size(RID p_probe, const Vector3 &p_size) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->size = p_size;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
void LightStorage::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->origin_offset = p_offset;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
void LightStorage::reflection_probe_set_as_interior(RID p_probe, bool p_enable) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->interior = p_enable;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
void LightStorage::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->box_projection = p_enable;
}
void LightStorage::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->enable_shadows = p_enable;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
void LightStorage::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->cull_mask = p_layers;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
void LightStorage::reflection_probe_set_reflection_mask(RID p_probe, uint32_t p_layers) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->reflection_mask = p_layers;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
void LightStorage::reflection_probe_set_resolution(RID p_probe, int p_resolution) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->resolution = p_resolution;
}
AABB LightStorage::reflection_probe_get_aabb(RID p_probe) const {
- return AABB();
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, AABB());
+
+ AABB aabb;
+ aabb.position = -reflection_probe->size / 2;
+ aabb.size = reflection_probe->size;
+
+ return aabb;
}
RS::ReflectionProbeUpdateMode LightStorage::reflection_probe_get_update_mode(RID p_probe) const {
- return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE;
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, RenderingServer::REFLECTION_PROBE_UPDATE_ONCE);
+
+ return reflection_probe->update_mode;
}
uint32_t LightStorage::reflection_probe_get_cull_mask(RID p_probe) const {
- return 0;
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, 0);
+
+ return reflection_probe->cull_mask;
}
uint32_t LightStorage::reflection_probe_get_reflection_mask(RID p_probe) const {
- return 0;
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, 0);
+
+ return reflection_probe->reflection_mask;
}
Vector3 LightStorage::reflection_probe_get_size(RID p_probe) const {
- return Vector3();
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, Vector3());
+
+ return reflection_probe->size;
}
Vector3 LightStorage::reflection_probe_get_origin_offset(RID p_probe) const {
- return Vector3();
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, Vector3());
+
+ return reflection_probe->origin_offset;
}
float LightStorage::reflection_probe_get_origin_max_distance(RID p_probe) const {
- return 0.0;
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, 0.0);
+
+ return reflection_probe->max_distance;
}
bool LightStorage::reflection_probe_renders_shadows(RID p_probe) const {
- return false;
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, false);
+
+ return reflection_probe->enable_shadows;
}
void LightStorage::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->mesh_lod_threshold = p_ratio;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const {
- return 0.0;
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, 0.0);
+
+ return reflection_probe->mesh_lod_threshold;
+}
+
+Dependency *LightStorage::reflection_probe_get_dependency(RID p_probe) const {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, nullptr);
+
+ return &reflection_probe->dependency;
}
/* REFLECTION ATLAS */
RID LightStorage::reflection_atlas_create() {
- return RID();
+ ReflectionAtlas ra;
+ ra.count = GLOBAL_GET("rendering/reflections/reflection_atlas/reflection_count");
+ ra.size = GLOBAL_GET("rendering/reflections/reflection_atlas/reflection_size");
+
+ return reflection_atlas_owner.make_rid(ra);
}
void LightStorage::reflection_atlas_free(RID p_ref_atlas) {
+ reflection_atlas_set_size(p_ref_atlas, 0, 0);
+
+ reflection_atlas_owner.free(p_ref_atlas);
}
int LightStorage::reflection_atlas_get_size(RID p_ref_atlas) const {
- return 0;
+ ReflectionAtlas *ra = reflection_atlas_owner.get_or_null(p_ref_atlas);
+ ERR_FAIL_NULL_V(ra, 0);
+
+ return ra->size;
}
void LightStorage::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) {
+ ReflectionAtlas *ra = reflection_atlas_owner.get_or_null(p_ref_atlas);
+ ERR_FAIL_NULL(ra);
+
+ if (ra->size == p_reflection_size && ra->count == p_reflection_count) {
+ return; //no changes
+ }
+
+ ra->size = p_reflection_size;
+ ra->count = p_reflection_count;
+
+ if (ra->depth != 0) {
+ //clear and invalidate everything
+ for (int i = 0; i < ra->reflections.size(); i++) {
+ for (int j = 0; j < 7; j++) {
+ if (ra->reflections[i].fbos[j] != 0) {
+ glDeleteFramebuffers(1, &ra->reflections[i].fbos[j]);
+ ra->reflections.write[i].fbos[j] = 0;
+ }
+ }
+
+ GLES3::Utilities::get_singleton()->texture_free_data(ra->reflections[i].color);
+ ra->reflections.write[i].color = 0;
+
+ GLES3::Utilities::get_singleton()->texture_free_data(ra->reflections[i].radiance);
+ ra->reflections.write[i].radiance = 0;
+
+ if (ra->reflections[i].owner.is_null()) {
+ continue;
+ }
+ reflection_probe_release_atlas_index(ra->reflections[i].owner);
+ //rp->atlasindex clear
+ }
+
+ ra->reflections.clear();
+
+ GLES3::Utilities::get_singleton()->texture_free_data(ra->depth);
+ ra->depth = 0;
+ }
+
+ if (ra->render_buffers.is_valid()) {
+ ra->render_buffers->free_render_buffer_data();
+ }
}
/* REFLECTION PROBE INSTANCE */
RID LightStorage::reflection_probe_instance_create(RID p_probe) {
- return RID();
+ ReflectionProbeInstance rpi;
+ rpi.probe = p_probe;
+
+ return reflection_probe_instance_owner.make_rid(rpi);
}
void LightStorage::reflection_probe_instance_free(RID p_instance) {
+ reflection_probe_release_atlas_index(p_instance);
+ reflection_probe_instance_owner.free(p_instance);
}
void LightStorage::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) {
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL(rpi);
+
+ rpi->transform = p_transform;
+ rpi->dirty = true;
}
bool LightStorage::reflection_probe_has_atlas_index(RID p_instance) {
- return false;
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, false);
+
+ if (rpi->atlas.is_null()) {
+ return false;
+ }
+
+ return rpi->atlas_index >= 0;
}
void LightStorage::reflection_probe_release_atlas_index(RID p_instance) {
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL(rpi);
+
+ if (rpi->atlas.is_null()) {
+ return; //nothing to release
+ }
+ ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(rpi->atlas);
+ ERR_FAIL_NULL(atlas);
+
+ ERR_FAIL_INDEX(rpi->atlas_index, atlas->reflections.size());
+ atlas->reflections.write[rpi->atlas_index].owner = RID();
+
+ if (rpi->rendering) {
+ // We were cancelled mid rendering, trigger refresh.
+ rpi->rendering = false;
+ rpi->dirty = true;
+ rpi->processing_layer = 0;
+ }
+
+ rpi->atlas_index = -1;
+ rpi->atlas = RID();
}
bool LightStorage::reflection_probe_instance_needs_redraw(RID p_instance) {
- return false;
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, false);
+
+ if (rpi->rendering) {
+ return false;
+ }
+
+ if (rpi->dirty) {
+ return true;
+ }
+
+ if (reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS) {
+ return true;
+ }
+
+ return rpi->atlas_index == -1;
}
bool LightStorage::reflection_probe_instance_has_reflection(RID p_instance) {
- return false;
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, false);
+
+ return rpi->atlas.is_valid();
}
bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) {
- return false;
+ TextureStorage *texture_storage = TextureStorage::get_singleton();
+ ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_reflection_atlas);
+
+ ERR_FAIL_NULL_V(atlas, false);
+
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, false);
+
+ if (atlas->render_buffers.is_null()) {
+ atlas->render_buffers.instantiate();
+ atlas->render_buffers->configure_for_probe(Size2i(atlas->size, atlas->size));
+ }
+
+ // First we check if our atlas is initialized.
+
+ // Not making an exception for update_mode = REFLECTION_PROBE_UPDATE_ALWAYS, we are using
+ // the same render techniques regardless of realtime or update once (for now).
+
+ if (atlas->depth == 0) {
+ // We need to create our textures
+ atlas->mipmap_count = Image::get_image_required_mipmaps(atlas->size, atlas->size, Image::FORMAT_RGBAH) - 1;
+ atlas->mipmap_count = MIN(atlas->mipmap_count, 8); // No more than 8 please..
+
+ glActiveTexture(GL_TEXTURE0);
+
+ {
+ // We create one set of 6 layers for depth, we can reuse this when rendering.
+ glGenTextures(1, &atlas->depth);
+ glBindTexture(GL_TEXTURE_2D_ARRAY, atlas->depth);
+
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, atlas->size, atlas->size, 6, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+
+ GLES3::Utilities::get_singleton()->texture_allocated_data(atlas->depth, atlas->size * atlas->size * 6 * 3, "Reflection probe atlas (depth)");
+ }
+
+ // Make room for our atlas entries
+ atlas->reflections.resize(atlas->count);
+
+ for (int i = 0; i < atlas->count; i++) {
+ // Create a cube map for this atlas entry
+ GLuint color = 0;
+ glGenTextures(1, &color);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, color);
+ atlas->reflections.write[i].color = color;
+
+#ifdef GL_API_ENABLED
+ if (RasterizerGLES3::is_gles_over_gl()) {
+ for (int s = 0; s < 6; s++) {
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + s, 0, GL_RGB10_A2, atlas->size, atlas->size, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, nullptr);
+ }
+ glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+ }
+#endif
+#ifdef GLES_API_ENABLED
+ if (!RasterizerGLES3::is_gles_over_gl()) {
+ glTexStorage2D(GL_TEXTURE_CUBE_MAP, atlas->mipmap_count, GL_RGB10_A2, atlas->size, atlas->size);
+ }
+#endif // GLES_API_ENABLED
+
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, atlas->mipmap_count - 1);
+
+ // Setup sizes and calculate how much memory we're using.
+ int mipmap_size = atlas->size;
+ uint32_t data_size = 0;
+ for (int m = 0; m < atlas->mipmap_count; m++) {
+ atlas->mipmap_size[m] = mipmap_size;
+ data_size += mipmap_size * mipmap_size * 6 * 4;
+ mipmap_size = MAX(mipmap_size >> 1, 1);
+ }
+
+ GLES3::Utilities::get_singleton()->texture_allocated_data(color, data_size, String("Reflection probe atlas (") + String::num_int64(i) + String(", color)"));
+
+ // Create a radiance map for this atlas entry
+ GLuint radiance = 0;
+ glGenTextures(1, &radiance);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, radiance);
+ atlas->reflections.write[i].radiance = radiance;
+
+#ifdef GL_API_ENABLED
+ if (RasterizerGLES3::is_gles_over_gl()) {
+ for (int s = 0; s < 6; s++) {
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + s, 0, GL_RGB10_A2, atlas->size, atlas->size, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, nullptr);
+ }
+ glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+ }
+#endif
+#ifdef GLES_API_ENABLED
+ if (!RasterizerGLES3::is_gles_over_gl()) {
+ glTexStorage2D(GL_TEXTURE_CUBE_MAP, atlas->mipmap_count, GL_RGB10_A2, atlas->size, atlas->size);
+ }
+#endif // GLES_API_ENABLED
+
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, atlas->mipmap_count - 1);
+
+ // Same data size as our color buffer
+ GLES3::Utilities::get_singleton()->texture_allocated_data(radiance, data_size, String("Reflection probe atlas (") + String::num_int64(i) + String(", radiance)"));
+
+ // Create our framebuffers so we can draw to all sides
+ for (int side = 0; side < 6; side++) {
+ GLuint fbo = 0;
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ // We use glFramebufferTexture2D for the color buffer as glFramebufferTextureLayer doesn't always work with cubemaps.
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, color, 0);
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, atlas->depth, 0, side);
+
+ // Validate framebuffer
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ WARN_PRINT("Could not create reflections framebuffer, status: " + texture_storage->get_framebuffer_error(status));
+ }
+
+ atlas->reflections.write[i].fbos[side] = fbo;
+ }
+
+ // Create an extra framebuffer for building our radiance
+ {
+ GLuint fbo = 0;
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ atlas->reflections.write[i].fbos[6] = fbo;
+ }
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+ glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
+ }
+
+ // Then we find a free slot for our reflection probe
+
+ if (rpi->atlas_index == -1) {
+ for (int i = 0; i < atlas->reflections.size(); i++) {
+ if (atlas->reflections[i].owner.is_null()) {
+ rpi->atlas_index = i;
+ break;
+ }
+ }
+ //find the one used last
+ if (rpi->atlas_index == -1) {
+ //everything is in use, find the one least used via LRU
+ uint64_t pass_min = 0;
+
+ for (int i = 0; i < atlas->reflections.size(); i++) {
+ ReflectionProbeInstance *rpi2 = reflection_probe_instance_owner.get_or_null(atlas->reflections[i].owner);
+ if (rpi2->last_pass < pass_min) {
+ pass_min = rpi2->last_pass;
+ rpi->atlas_index = i;
+ }
+ }
+ }
+ }
+
+ if (rpi->atlas_index != -1) { // should we fail if this is still -1 ?
+ atlas->reflections.write[rpi->atlas_index].owner = p_instance;
+ }
+
+ rpi->atlas = p_reflection_atlas;
+ rpi->rendering = true;
+ rpi->dirty = false;
+ rpi->processing_layer = 0;
+
+ return true;
}
Ref<RenderSceneBuffers> LightStorage::reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) {
- return Ref<RenderSceneBuffers>();
+ ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_reflection_atlas);
+ ERR_FAIL_NULL_V(atlas, Ref<RenderSceneBuffersGLES3>());
+
+ return atlas->render_buffers;
}
bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) {
- return true;
+ GLES3::CubemapFilter *cubemap_filter = GLES3::CubemapFilter::get_singleton();
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, false);
+ ERR_FAIL_COND_V(!rpi->rendering, false);
+ ERR_FAIL_COND_V(rpi->atlas.is_null(), false);
+
+ ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(rpi->atlas);
+ if (!atlas || rpi->atlas_index == -1) {
+ //does not belong to an atlas anymore, cancel (was removed from atlas or atlas changed while rendering)
+ rpi->rendering = false;
+ rpi->processing_layer = 0;
+ return false;
+ }
+
+ if (LightStorage::get_singleton()->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS) {
+ // Using real time reflections, all roughness is done in one step
+ for (int m = 0; m < atlas->mipmap_count; m++) {
+ const GLES3::ReflectionAtlas::Reflection &reflection = atlas->reflections[rpi->atlas_index];
+ cubemap_filter->filter_radiance(reflection.color, reflection.radiance, reflection.fbos[6], atlas->size, atlas->mipmap_count, m);
+ }
+
+ rpi->rendering = false;
+ rpi->processing_layer = 0;
+ return true;
+ } else {
+ const GLES3::ReflectionAtlas::Reflection &reflection = atlas->reflections[rpi->atlas_index];
+ cubemap_filter->filter_radiance(reflection.color, reflection.radiance, reflection.fbos[6], atlas->size, atlas->mipmap_count, rpi->processing_layer);
+
+ rpi->processing_layer++;
+ if (rpi->processing_layer == atlas->mipmap_count) {
+ rpi->rendering = false;
+ rpi->processing_layer = 0;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+GLuint LightStorage::reflection_probe_instance_get_texture(RID p_instance) {
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, 0);
+
+ ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(rpi->atlas);
+ ERR_FAIL_NULL_V(atlas, 0);
+
+ return atlas->reflections[rpi->atlas_index].radiance;
+}
+
+GLuint LightStorage::reflection_probe_instance_get_framebuffer(RID p_instance, int p_index) {
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, 0);
+ ERR_FAIL_INDEX_V(p_index, 6, 0);
+
+ ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(rpi->atlas);
+ ERR_FAIL_NULL_V(atlas, 0);
+ return atlas->reflections[rpi->atlas_index].fbos[p_index];
}
/* LIGHTMAP CAPTURE */
@@ -1020,7 +1475,7 @@ bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_i
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, texture_id, 0);
@@ -1042,7 +1497,7 @@ bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_i
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture_id, 0);
@@ -1064,7 +1519,11 @@ bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_i
for (int j = 0; j < sc; j++) {
LightInstance *sli = light_instance_owner.get_or_null(sarr[j].owner);
- ERR_CONTINUE(!sli);
+ if (!sli) {
+ // Found a released light instance.
+ found_used_idx = j;
+ break;
+ }
if (sli->last_scene_pass != RasterizerSceneGLES3::get_singleton()->get_scene_pass()) {
// Was just allocated, don't kill it so soon, wait a bit.
@@ -1128,14 +1587,14 @@ void LightStorage::update_directional_shadow_atlas() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0);
}
glUseProgram(0);
glDepthMask(GL_TRUE);
glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo);
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
glClear(GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, 0);
diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h
index 51c5c48106..b6e64c9492 100644
--- a/drivers/gles3/storage/light_storage.h
+++ b/drivers/gles3/storage/light_storage.h
@@ -34,6 +34,7 @@
#ifdef GLES3_ENABLED
#include "platform_gl.h"
+#include "render_scene_buffers_gles3.h"
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
@@ -126,12 +127,51 @@ struct ReflectionProbe {
bool box_projection = false;
bool enable_shadows = false;
uint32_t cull_mask = (1 << 20) - 1;
+ uint32_t reflection_mask = (1 << 20) - 1;
float mesh_lod_threshold = 0.01;
float baked_exposure = 1.0;
Dependency dependency;
};
+/* REFLECTION ATLAS */
+
+struct ReflectionAtlas {
+ int count = 0;
+ int size = 0;
+
+ int mipmap_count = 1; // number of mips, including original
+ int mipmap_size[8];
+ GLuint depth = 0;
+
+ struct Reflection {
+ RID owner;
+ GLuint color = 0;
+ GLuint radiance = 0;
+ GLuint fbos[7];
+ };
+ Vector<Reflection> reflections;
+
+ Ref<RenderSceneBuffersGLES3> render_buffers; // Further render buffers used.
+};
+
+/* REFLECTION PROBE INSTANCE */
+
+struct ReflectionProbeInstance {
+ RID probe;
+ int atlas_index = -1;
+ RID atlas;
+
+ bool dirty = true;
+ bool rendering = false;
+ int processing_layer = 0;
+
+ uint64_t last_pass = 0;
+ uint32_t cull_mask = 0;
+
+ Transform3D transform;
+};
+
/* LIGHTMAP */
struct Lightmap {
@@ -181,6 +221,13 @@ private:
/* REFLECTION PROBE */
mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner;
+ /* REFLECTION ATLAS */
+ mutable RID_Owner<ReflectionAtlas> reflection_atlas_owner;
+
+ /* REFLECTION PROBE INSTANCE */
+
+ mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner;
+
/* LIGHTMAP */
Vector<RID> lightmap_textures;
@@ -394,6 +441,29 @@ public:
virtual void light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override;
virtual void light_instance_mark_visible(RID p_light_instance) override;
+ virtual bool light_instance_is_shadow_visible_at_position(RID p_light_instance, const Vector3 &p_position) const override {
+ const LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
+ ERR_FAIL_NULL_V(light_instance, false);
+ const Light *light = light_owner.get_or_null(light_instance->light);
+ ERR_FAIL_NULL_V(light, false);
+
+ if (!light->shadow) {
+ return false;
+ }
+
+ if (!light->distance_fade) {
+ return true;
+ }
+
+ real_t distance = p_position.distance_to(light_instance->transform.origin);
+
+ if (distance > light->distance_fade_shadow + light->distance_fade_length) {
+ return false;
+ }
+
+ return true;
+ }
+
_FORCE_INLINE_ RID light_instance_get_base_light(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->light;
@@ -559,6 +629,9 @@ public:
/* PROBE API */
+ ReflectionProbe *get_reflection_probe(RID p_rid) { return reflection_probe_owner.get_or_null(p_rid); };
+ bool owns_reflection_probe(RID p_rid) { return reflection_probe_owner.owns(p_rid); };
+
virtual RID reflection_probe_allocate() override;
virtual void reflection_probe_initialize(RID p_rid) override;
virtual void reflection_probe_free(RID p_rid) override;
@@ -589,8 +662,12 @@ public:
virtual float reflection_probe_get_origin_max_distance(RID p_probe) const override;
virtual bool reflection_probe_renders_shadows(RID p_probe) const override;
+ Dependency *reflection_probe_get_dependency(RID p_probe) const;
+
/* REFLECTION ATLAS */
+ bool owns_reflection_atlas(RID p_rid) { return reflection_atlas_owner.owns(p_rid); }
+
virtual RID reflection_atlas_create() override;
virtual void reflection_atlas_free(RID p_ref_atlas) override;
virtual int reflection_atlas_get_size(RID p_ref_atlas) const override;
@@ -598,6 +675,8 @@ public:
/* REFLECTION PROBE INSTANCE */
+ bool owns_reflection_probe_instance(RID p_rid) { return reflection_probe_instance_owner.owns(p_rid); }
+
virtual RID reflection_probe_instance_create(RID p_probe) override;
virtual void reflection_probe_instance_free(RID p_instance) override;
virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override;
@@ -609,6 +688,27 @@ public:
virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) override;
virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override;
+ _FORCE_INLINE_ RID reflection_probe_instance_get_probe(RID p_instance) {
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, RID());
+
+ return rpi->probe;
+ }
+ _FORCE_INLINE_ RID reflection_probe_instance_get_atlas(RID p_instance) {
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, RID());
+
+ return rpi->atlas;
+ }
+ Transform3D reflection_probe_instance_get_transform(RID p_instance) {
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+ ERR_FAIL_NULL_V(rpi, Transform3D());
+
+ return rpi->transform;
+ }
+ GLuint reflection_probe_instance_get_texture(RID p_instance);
+ GLuint reflection_probe_instance_get_framebuffer(RID p_instance, int p_index);
+
/* LIGHTMAP CAPTURE */
Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); };
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 23376b4381..996c205042 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1243,6 +1243,7 @@ MaterialStorage::MaterialStorage() {
actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
actions.renames["ALBEDO"] = "albedo";
actions.renames["ALPHA"] = "alpha";
+ actions.renames["PREMUL_ALPHA_FACTOR"] = "premul_alpha";
actions.renames["METALLIC"] = "metallic";
actions.renames["SPECULAR"] = "specular";
actions.renames["ROUGHNESS"] = "roughness";
@@ -1274,6 +1275,7 @@ MaterialStorage::MaterialStorage() {
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
+ actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz";
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
@@ -1320,11 +1322,13 @@ MaterialStorage::MaterialStorage() {
actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";
+ actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX_USED\n";
actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n";
actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n";
actions.usage_defines["ALPHA_ANTIALIASING_EDGE"] = "#define ALPHA_ANTIALIASING_EDGE_USED\n";
actions.usage_defines["ALPHA_TEXTURE_COORDINATE"] = "@ALPHA_ANTIALIASING_EDGE";
+ actions.usage_defines["PREMULT_ALPHA_FACTOR"] = "#define PREMULT_ALPHA_USED";
actions.usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n";
actions.usage_defines["SSS_TRANSMITTANCE_DEPTH"] = "#define ENABLE_TRANSMITTANCE\n";
@@ -1962,13 +1966,9 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture
Variant value = d["value"];
if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) {
- //textire
- if (!p_load_textures) {
- continue;
- }
-
String path = value;
- if (path.is_empty()) {
+ // Don't load the textures, but still add the parameter so shaders compile correctly while loading.
+ if (!p_load_textures || path.is_empty()) {
value = RID();
} else {
Ref<Resource> resource = ResourceLoader::load(path);
@@ -2906,6 +2906,7 @@ void SceneShaderData::set_code(const String &p_code) {
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MIX);
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_modei, BLEND_MODE_SUB);
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MUL);
+ actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_modei, BLEND_MODE_PREMULT_ALPHA);
actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h
index 59f5682362..392ebcc570 100644
--- a/drivers/gles3/storage/material_storage.h
+++ b/drivers/gles3/storage/material_storage.h
@@ -43,7 +43,6 @@
#include "servers/rendering/storage/utilities.h"
#include "drivers/gles3/shaders/canvas.glsl.gen.h"
-#include "drivers/gles3/shaders/cubemap_filter.glsl.gen.h"
#include "drivers/gles3/shaders/particles.glsl.gen.h"
#include "drivers/gles3/shaders/scene.glsl.gen.h"
#include "drivers/gles3/shaders/sky.glsl.gen.h"
@@ -249,6 +248,7 @@ struct SceneShaderData : public ShaderData {
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
+ BLEND_MODE_PREMULT_ALPHA,
BLEND_MODE_ALPHA_TO_COVERAGE
};
@@ -543,7 +543,6 @@ public:
CanvasShaderGLES3 canvas_shader;
SkyShaderGLES3 sky_shader;
SceneShaderGLES3 scene_shader;
- CubemapFilterShaderGLES3 cubemap_filter_shader;
ParticlesShaderGLES3 particles_process_shader;
ShaderCompiler compiler_canvas;
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index e073db3cfd..d8a5b960b8 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -1936,6 +1936,11 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
glBindBuffer(GL_ARRAY_BUFFER, 0);
} else {
+ // If we have a data cache, just update it.
+ if (multimesh->data_cache.size()) {
+ multimesh->data_cache = p_buffer;
+ }
+
// Only Transform is being used, so we can upload directly.
ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache));
const float *r = p_buffer.ptr();
@@ -1947,16 +1952,12 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
multimesh->buffer_set = true;
if (multimesh->data_cache.size() || multimesh->uses_colors || multimesh->uses_custom_data) {
- //if we have a data cache, just update it
- multimesh->data_cache = multimesh->data_cache;
- {
- //clear dirty since nothing will be dirty anymore
- uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, MULTIMESH_DIRTY_REGION_SIZE);
- for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) {
- multimesh->data_cache_dirty_regions[i] = false;
- }
- multimesh->data_cache_used_dirty_regions = 0;
+ // Clear dirty since nothing will be dirty anymore.
+ uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, MULTIMESH_DIRTY_REGION_SIZE);
+ for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) {
+ multimesh->data_cache_dirty_regions[i] = false;
}
+ multimesh->data_cache_used_dirty_regions = 0;
_multimesh_mark_all_dirty(multimesh, false, true); //update AABB
} else if (multimesh->mesh.is_valid()) {
diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp
index b72b4eaf8d..fee90599e0 100644
--- a/drivers/gles3/storage/particles_storage.cpp
+++ b/drivers/gles3/storage/particles_storage.cpp
@@ -31,6 +31,8 @@
#ifdef GLES3_ENABLED
#include "particles_storage.h"
+
+#include "config.h"
#include "material_storage.h"
#include "mesh_storage.h"
#include "texture_storage.h"
@@ -120,6 +122,8 @@ void ParticlesStorage::particles_set_mode(RID p_particles, RS::ParticlesMode p_m
}
void ParticlesStorage::particles_set_emitting(RID p_particles, bool p_emitting) {
+ ERR_FAIL_COND_MSG(GLES3::Config::get_singleton()->adreno_3xx_compatibility, "Due to driver bugs, GPUParticles are not supported on Adreno 3XX devices. Please use CPUParticles instead.");
+
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL(particles);
@@ -127,7 +131,10 @@ void ParticlesStorage::particles_set_emitting(RID p_particles, bool p_emitting)
}
bool ParticlesStorage::particles_get_emitting(RID p_particles) {
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
+ if (GLES3::Config::get_singleton()->adreno_3xx_compatibility) {
+ return false;
+ }
+
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
@@ -372,10 +379,6 @@ void ParticlesStorage::particles_request_process(RID p_particles) {
}
AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care.");
- }
-
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, AABB());
@@ -1003,6 +1006,12 @@ void ParticlesStorage::_particles_update_instance_buffer(Particles *particles, c
}
void ParticlesStorage::update_particles() {
+ if (!particle_update_list.first()) {
+ // Return early to avoid unnecessary state changes.
+ return;
+ }
+
+ RENDER_TIMESTAMP("Update GPUParticles");
glEnable(GL_RASTERIZER_DISCARD);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
@@ -1193,7 +1202,6 @@ Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const {
}
bool ParticlesStorage::particles_is_inactive(RID p_particles) const {
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
return !particles->emitting && particles->inactive;
diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp
index de0a64f5fe..cb194933ed 100644
--- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp
+++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp
@@ -405,6 +405,13 @@ void RenderSceneBuffersGLES3::_check_render_buffers() {
}
}
+void RenderSceneBuffersGLES3::configure_for_probe(Size2i p_size) {
+ internal_size = p_size;
+ target_size = p_size;
+ scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
+ view_count = 1;
+}
+
void RenderSceneBuffersGLES3::_clear_msaa3d_buffers() {
for (const FBDEF &cached_fbo : msaa3d.cached_fbos) {
GLuint fbo = cached_fbo.fbo;
@@ -570,8 +577,7 @@ void RenderSceneBuffersGLES3::check_glow_buffers() {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
Size2i level_size = internal_size;
for (int i = 0; i < 4; i++) {
- level_size.x = MAX(level_size.x >> 1, 4);
- level_size.y = MAX(level_size.y >> 1, 4);
+ level_size = Size2i(level_size.x >> 1, level_size.y >> 1).maxi(4);
glow.levels[i].size = level_size;
diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h
index 8d03d3438d..04b9113b91 100644
--- a/drivers/gles3/storage/render_scene_buffers_gles3.h
+++ b/drivers/gles3/storage/render_scene_buffers_gles3.h
@@ -101,6 +101,7 @@ public:
RenderSceneBuffersGLES3();
virtual ~RenderSceneBuffersGLES3();
virtual void configure(const RenderSceneBuffersConfiguration *p_config) override;
+ void configure_for_probe(Size2i p_size);
virtual void set_fsr_sharpness(float p_fsr_sharpness) override{};
virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override{};
@@ -109,7 +110,7 @@ public:
void free_render_buffer_data();
void check_backbuffer(bool p_need_color, bool p_need_depth); // Check if we need to initialize our backbuffer.
- void check_glow_buffers(); // Check if we need to initialise our glow buffers.
+ void check_glow_buffers(); // Check if we need to initialize our glow buffers.
GLuint get_render_fbo();
GLuint get_msaa3d_fbo() {
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index c955b3f708..2dcf623995 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1077,7 +1077,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
- glDepthFunc(GL_LEQUAL);
+ glDepthFunc(GL_GEQUAL);
glColorMask(1, 1, 1, 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture->tex_id);
@@ -1391,7 +1391,7 @@ void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) {
tinfo.format = t->format;
tinfo.width = t->alloc_width;
tinfo.height = t->alloc_height;
- tinfo.depth = 0;
+ tinfo.depth = t->depth;
tinfo.bytes = t->total_data_size;
r_info->push_back(tinfo);
}
@@ -1495,14 +1495,18 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image,
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]);
+ if (texture->target == GL_TEXTURE_2D_ARRAY) {
+ if (p_initialize) {
+ glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0,
+ size * texture->layers, &read[ofs]);
+ } else {
+ glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]);
+ }
+ } else {
+ glCompressedTexImage2D(blit_target, i, internal_format, w, h, 0, size, &read[ofs]);
+ }
} else {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (texture->target == GL_TEXTURE_2D_ARRAY) {
@@ -2312,9 +2316,11 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
if (rt->backbuffer_fbo != 0) {
glDeleteFramebuffers(1, &rt->backbuffer_fbo);
+ rt->backbuffer_fbo = 0;
+ }
+ if (rt->backbuffer != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(rt->backbuffer);
rt->backbuffer = 0;
- rt->backbuffer_fbo = 0;
}
if (rt->backbuffer_depth != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(rt->backbuffer_depth);
@@ -2812,7 +2818,7 @@ void TextureStorage::_render_target_allocate_sdf(RenderTarget *rt) {
}
rt->process_size = size * scale / 100;
- rt->process_size = rt->process_size.max(Size2i(1, 1));
+ rt->process_size = rt->process_size.maxi(1);
glGenTextures(2, rt->sdf_texture_process);
glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[0]);
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index ef310262c7..8a03d72b9b 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -672,6 +672,8 @@ public:
virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {}
virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; }
+ virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override {}
+ virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_UPDATE_DISABLED; }
virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {}
virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); }
diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp
index c4fbe098cd..356dc06733 100644
--- a/drivers/gles3/storage/utilities.cpp
+++ b/drivers/gles3/storage/utilities.cpp
@@ -158,6 +158,8 @@ RS::InstanceType Utilities::get_base_type(RID p_rid) const {
return RS::INSTANCE_LIGHTMAP;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
return RS::INSTANCE_PARTICLES;
+ } else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe(p_rid)) {
+ return RS::INSTANCE_REFLECTION_PROBE;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) {
return RS::INSTANCE_PARTICLES_COLLISION;
} else if (owns_visibility_notifier(p_rid)) {
@@ -197,6 +199,15 @@ bool Utilities::free(RID p_rid) {
} else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) {
GLES3::LightStorage::get_singleton()->lightmap_free(p_rid);
return true;
+ } else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe(p_rid)) {
+ GLES3::LightStorage::get_singleton()->reflection_probe_free(p_rid);
+ return true;
+ } else if (GLES3::LightStorage::get_singleton()->owns_reflection_atlas(p_rid)) {
+ GLES3::LightStorage::get_singleton()->reflection_atlas_free(p_rid);
+ return true;
+ } else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe_instance(p_rid)) {
+ GLES3::LightStorage::get_singleton()->reflection_probe_instance_free(p_rid);
+ return true;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
GLES3::ParticlesStorage::get_singleton()->particles_free(p_rid);
return true;
@@ -229,6 +240,9 @@ void Utilities::base_update_dependency(RID p_base, DependencyTracker *p_instance
if (multimesh->mesh.is_valid()) {
base_update_dependency(multimesh->mesh, p_instance);
}
+ } else if (LightStorage::get_singleton()->owns_reflection_probe(p_base)) {
+ Dependency *dependency = LightStorage::get_singleton()->reflection_probe_get_dependency(p_base);
+ p_instance->update_dependency(dependency);
} else if (LightStorage::get_singleton()->owns_light(p_base)) {
Light *l = LightStorage::get_singleton()->get_light(p_base);
p_instance->update_dependency(&l->dependency);
@@ -285,7 +299,7 @@ void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_de
ERR_FAIL_NULL(vn);
if (p_enter) {
- if (!vn->enter_callback.is_null()) {
+ if (vn->enter_callback.is_valid()) {
if (p_deferred) {
vn->enter_callback.call_deferred();
} else {
@@ -293,7 +307,7 @@ void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_de
}
}
} else {
- if (!vn->exit_callback.is_null()) {
+ if (vn->exit_callback.is_valid()) {
if (p_deferred) {
vn->exit_callback.call_deferred();
} else {
diff --git a/drivers/png/SCsub b/drivers/png/SCsub
index dd4777a19b..e38f3c4760 100644
--- a/drivers/png/SCsub
+++ b/drivers/png/SCsub
@@ -39,7 +39,7 @@ if env["builtin_libpng"]:
if env["arch"].startswith("arm"):
if env.msvc: # Can't compile assembly files with MSVC.
- env_thirdparty.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT"), 0])
+ env_thirdparty.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT", 0)])
else:
env_neon = env_thirdparty.Clone()
if "S_compiler" in env:
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 20382e7f7c..669e6c2aa9 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -183,7 +183,7 @@ Error AudioDriverPulseAudio::init_output_device() {
// If there is a specified output device, check that it is really present
if (output_device_name != "Default") {
PackedStringArray list = get_output_device_list();
- if (list.find(output_device_name) == -1) {
+ if (!list.has(output_device_name)) {
output_device_name = "Default";
new_output_device = "Default";
}
@@ -695,7 +695,7 @@ Error AudioDriverPulseAudio::init_input_device() {
// If there is a specified input device, check that it is really present
if (input_device_name != "Default") {
PackedStringArray list = get_input_device_list();
- if (list.find(input_device_name) == -1) {
+ if (!list.has(input_device_name)) {
input_device_name = "Default";
new_input_device = "Default";
}
diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp
index 46efb45934..a4208829b7 100644
--- a/drivers/unix/dir_access_unix.cpp
+++ b/drivers/unix/dir_access_unix.cpp
@@ -289,7 +289,7 @@ String DirAccessUnix::get_drive(int p_drive) {
ERR_FAIL_INDEX_V(p_drive, list.size(), "");
- return list[p_drive];
+ return list.get(p_drive);
}
int DirAccessUnix::get_current_drive() {
@@ -419,7 +419,7 @@ Error DirAccessUnix::remove(String p_path) {
return FAILED;
}
- if (S_ISDIR(flags.st_mode)) {
+ if (S_ISDIR(flags.st_mode) && !is_link(p_path)) {
return ::rmdir(p_path.utf8().get_data()) == 0 ? OK : FAILED;
} else {
return ::unlink(p_path.utf8().get_data()) == 0 ? OK : FAILED;
@@ -435,7 +435,7 @@ bool DirAccessUnix::is_link(String p_file) {
struct stat flags = {};
if ((lstat(p_file.utf8().get_data(), &flags) != 0)) {
- return FAILED;
+ return false;
}
return S_ISLNK(flags.st_mode);
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index a35d8bfdde..210507c2c6 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -286,6 +286,23 @@ Error FileAccessUnix::get_error() const {
return last_error;
}
+Error FileAccessUnix::resize(int64_t p_length) {
+ ERR_FAIL_NULL_V_MSG(f, FAILED, "File must be opened before use.");
+ int res = ::ftruncate(fileno(f), p_length);
+ switch (res) {
+ case 0:
+ return OK;
+ case EBADF:
+ return ERR_FILE_CANT_OPEN;
+ case EFBIG:
+ return ERR_OUT_OF_MEMORY;
+ case EINVAL:
+ return ERR_INVALID_PARAMETER;
+ default:
+ return FAILED;
+ }
+}
+
void FileAccessUnix::flush() {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
fflush(f);
diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h
index 553fbcf355..c0286dbff3 100644
--- a/drivers/unix/file_access_unix.h
+++ b/drivers/unix/file_access_unix.h
@@ -75,6 +75,7 @@ public:
virtual Error get_error() const override; ///< get last error
+ virtual Error resize(int64_t p_length) override;
virtual void flush() override;
virtual void store_8(uint8_t p_dest) override; ///< store a byte
virtual void store_16(uint16_t p_dest) override;
diff --git a/drivers/unix/file_access_unix_pipe.cpp b/drivers/unix/file_access_unix_pipe.cpp
new file mode 100644
index 0000000000..5d9a27ad05
--- /dev/null
+++ b/drivers/unix/file_access_unix_pipe.cpp
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* file_access_unix_pipe.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "file_access_unix_pipe.h"
+
+#if defined(UNIX_ENABLED)
+
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd) {
+ // Open pipe using handles created by pipe(fd) call in the OS.execute_with_pipe.
+ _close();
+
+ path_src = String();
+ unlink_on_close = false;
+ ERR_FAIL_COND_V_MSG(fd[0] >= 0 || fd[1] >= 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+ fd[0] = p_rfd;
+ fd[1] = p_wfd;
+
+ last_error = OK;
+ return OK;
+}
+
+Error FileAccessUnixPipe::open_internal(const String &p_path, int p_mode_flags) {
+ _close();
+
+ path_src = p_path;
+ ERR_FAIL_COND_V_MSG(fd[0] >= 0 || fd[1] >= 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+
+ path = String("/tmp/") + p_path.replace("pipe://", "").replace("/", "_");
+ struct stat st = {};
+ int err = stat(path.utf8().get_data(), &st);
+ if (err) {
+ if (mkfifo(path.utf8().get_data(), 0666) != 0) {
+ last_error = ERR_FILE_CANT_OPEN;
+ return last_error;
+ }
+ unlink_on_close = true;
+ } else {
+ ERR_FAIL_COND_V_MSG(!S_ISFIFO(st.st_mode), ERR_ALREADY_IN_USE, "Pipe name is already used by file.");
+ }
+
+ int f = ::open(path.utf8().get_data(), O_RDWR | O_CLOEXEC);
+ if (f < 0) {
+ switch (errno) {
+ case ENOENT: {
+ last_error = ERR_FILE_NOT_FOUND;
+ } break;
+ default: {
+ last_error = ERR_FILE_CANT_OPEN;
+ } break;
+ }
+ return last_error;
+ }
+
+ // Set close on exec to avoid leaking it to subprocesses.
+ fd[0] = f;
+ fd[1] = f;
+
+ last_error = OK;
+ return OK;
+}
+
+void FileAccessUnixPipe::_close() {
+ if (fd[0] < 0) {
+ return;
+ }
+
+ if (fd[1] != fd[0]) {
+ ::close(fd[1]);
+ }
+ ::close(fd[0]);
+ fd[0] = -1;
+ fd[1] = -1;
+
+ if (unlink_on_close) {
+ ::unlink(path.utf8().ptr());
+ }
+ unlink_on_close = false;
+}
+
+bool FileAccessUnixPipe::is_open() const {
+ return (fd[0] >= 0 || fd[1] >= 0);
+}
+
+String FileAccessUnixPipe::get_path() const {
+ return path_src;
+}
+
+String FileAccessUnixPipe::get_path_absolute() const {
+ return path_src;
+}
+
+uint8_t FileAccessUnixPipe::get_8() const {
+ ERR_FAIL_COND_V_MSG(fd[0] < 0, 0, "Pipe must be opened before use.");
+
+ uint8_t b;
+ if (::read(fd[0], &b, 1) == 0) {
+ last_error = ERR_FILE_CANT_READ;
+ b = '\0';
+ } else {
+ last_error = OK;
+ }
+ return b;
+}
+
+uint64_t FileAccessUnixPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
+ ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
+ ERR_FAIL_COND_V_MSG(fd[0] < 0, -1, "Pipe must be opened before use.");
+
+ uint64_t read = ::read(fd[0], p_dst, p_length);
+ if (read == p_length) {
+ last_error = ERR_FILE_CANT_READ;
+ } else {
+ last_error = OK;
+ }
+ return read;
+}
+
+Error FileAccessUnixPipe::get_error() const {
+ return last_error;
+}
+
+void FileAccessUnixPipe::store_8(uint8_t p_src) {
+ ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
+ if (::write(fd[1], &p_src, 1) != 1) {
+ last_error = ERR_FILE_CANT_WRITE;
+ } else {
+ last_error = OK;
+ }
+}
+
+void FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
+ ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
+ ERR_FAIL_COND(!p_src && p_length > 0);
+ if (::write(fd[1], p_src, p_length) != (ssize_t)p_length) {
+ last_error = ERR_FILE_CANT_WRITE;
+ } else {
+ last_error = OK;
+ }
+}
+
+void FileAccessUnixPipe::close() {
+ _close();
+}
+
+FileAccessUnixPipe::~FileAccessUnixPipe() {
+ _close();
+}
+
+#endif // UNIX_ENABLED
diff --git a/drivers/unix/file_access_unix_pipe.h b/drivers/unix/file_access_unix_pipe.h
new file mode 100644
index 0000000000..8e7988791b
--- /dev/null
+++ b/drivers/unix/file_access_unix_pipe.h
@@ -0,0 +1,97 @@
+/**************************************************************************/
+/* file_access_unix_pipe.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef FILE_ACCESS_UNIX_PIPE_H
+#define FILE_ACCESS_UNIX_PIPE_H
+
+#include "core/io/file_access.h"
+#include "core/os/memory.h"
+
+#include <stdio.h>
+
+#if defined(UNIX_ENABLED)
+
+class FileAccessUnixPipe : public FileAccess {
+ bool unlink_on_close = false;
+
+ int fd[2] = { -1, -1 };
+
+ mutable Error last_error = OK;
+ String path;
+ String path_src;
+
+ void _close();
+
+public:
+ Error open_existing(int p_rfd, int p_wfd);
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
+
+ virtual bool is_open() const override; ///< true when file is open
+
+ virtual String get_path() const override; /// returns the path for the current open file
+ virtual String get_path_absolute() const override; /// returns the absolute path for the current open file
+
+ virtual void seek(uint64_t p_position) override {}
+ virtual void seek_end(int64_t p_position = 0) override {}
+ virtual uint64_t get_position() const override { return 0; }
+ virtual uint64_t get_length() const override { return 0; }
+
+ virtual bool eof_reached() const override { return false; }
+
+ virtual uint8_t get_8() const override; ///< get a byte
+ virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
+
+ virtual Error get_error() const override; ///< get last error
+
+ virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
+ virtual void flush() override {}
+ virtual void store_8(uint8_t p_src) override; ///< store a byte
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
+
+ virtual bool file_exists(const String &p_path) override { return false; }
+
+ virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
+
+ virtual void close() override;
+
+ FileAccessUnixPipe() {}
+ virtual ~FileAccessUnixPipe();
+};
+
+#endif // UNIX_ENABLED
+
+#endif // FILE_ACCESS_UNIX_PIPE_H
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 74b703b09e..ce2553456d 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -37,6 +37,7 @@
#include "core/debugger/script_debugger.h"
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
+#include "drivers/unix/file_access_unix_pipe.h"
#include "drivers/unix/net_socket_posix.h"
#include "drivers/unix/thread_posix.h"
#include "servers/rendering_server.h"
@@ -160,17 +161,20 @@ void OS_Unix::initialize_core() {
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
+ FileAccess::make_default<FileAccessUnixPipe>(FileAccess::ACCESS_PIPE);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
NetSocketPosix::make_default();
IPUnix::make_default();
+ process_map = memnew((HashMap<ProcessID, ProcessInfo>));
_setup_clock();
}
void OS_Unix::finalize_core() {
+ memdelete(process_map);
NetSocketPosix::cleanup();
}
@@ -489,6 +493,111 @@ Dictionary OS_Unix::get_memory_info() const {
return meminfo;
}
+Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &p_arguments) {
+#define CLEAN_PIPES \
+ if (pipe_in[0] >= 0) { \
+ ::close(pipe_in[0]); \
+ } \
+ if (pipe_in[1] >= 0) { \
+ ::close(pipe_in[1]); \
+ } \
+ if (pipe_out[0] >= 0) { \
+ ::close(pipe_out[0]); \
+ } \
+ if (pipe_out[1] >= 0) { \
+ ::close(pipe_out[1]); \
+ } \
+ if (pipe_err[0] >= 0) { \
+ ::close(pipe_err[0]); \
+ } \
+ if (pipe_err[1] >= 0) { \
+ ::close(pipe_err[1]); \
+ }
+
+ Dictionary ret;
+#ifdef __EMSCRIPTEN__
+ // Don't compile this code at all to avoid undefined references.
+ // Actual virtual call goes to OS_Web.
+ ERR_FAIL_V(ret);
+#else
+ // Create pipes.
+ int pipe_in[2] = { -1, -1 };
+ int pipe_out[2] = { -1, -1 };
+ int pipe_err[2] = { -1, -1 };
+
+ ERR_FAIL_COND_V(pipe(pipe_in) != 0, ret);
+ if (pipe(pipe_out) != 0) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+ if (pipe(pipe_err) != 0) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+
+ // Create process.
+ pid_t pid = fork();
+ if (pid < 0) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+
+ if (pid == 0) {
+ // The child process.
+ Vector<CharString> cs;
+ cs.push_back(p_path.utf8());
+ for (const String &arg : p_arguments) {
+ cs.push_back(arg.utf8());
+ }
+
+ Vector<char *> args;
+ for (int i = 0; i < cs.size(); i++) {
+ args.push_back((char *)cs[i].get_data());
+ }
+ args.push_back(0);
+
+ ::close(STDIN_FILENO);
+ ::dup2(pipe_in[0], STDIN_FILENO);
+
+ ::close(STDOUT_FILENO);
+ ::dup2(pipe_out[1], STDOUT_FILENO);
+
+ ::close(STDERR_FILENO);
+ ::dup2(pipe_err[1], STDERR_FILENO);
+
+ CLEAN_PIPES
+
+ execvp(p_path.utf8().get_data(), &args[0]);
+ // The execvp() function only returns if an error occurs.
+ ERR_PRINT("Could not create child process: " + p_path);
+ raise(SIGKILL);
+ }
+ ::close(pipe_in[0]);
+ ::close(pipe_out[1]);
+ ::close(pipe_err[1]);
+
+ Ref<FileAccessUnixPipe> main_pipe;
+ main_pipe.instantiate();
+ main_pipe->open_existing(pipe_out[0], pipe_in[1]);
+
+ Ref<FileAccessUnixPipe> err_pipe;
+ err_pipe.instantiate();
+ err_pipe->open_existing(pipe_err[0], 0);
+
+ ProcessInfo pi;
+ process_map_mutex.lock();
+ process_map->insert(pid, pi);
+ process_map_mutex.unlock();
+
+ ret["stdio"] = main_pipe;
+ ret["stderr"] = err_pipe;
+ ret["pid"] = pid;
+
+#undef CLEAN_PIPES
+ return ret;
+#endif
+}
+
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.
@@ -497,8 +606,8 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, St
#else
if (r_pipe) {
String command = "\"" + p_path + "\"";
- for (int i = 0; i < p_arguments.size(); i++) {
- command += String(" \"") + p_arguments[i] + "\"";
+ for (const String &arg : p_arguments) {
+ command += String(" \"") + arg + "\"";
}
if (read_stderr) {
command += " 2>&1"; // Include stderr
@@ -538,8 +647,8 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, St
// The child process
Vector<CharString> cs;
cs.push_back(p_path.utf8());
- for (int i = 0; i < p_arguments.size(); i++) {
- cs.push_back(p_arguments[i].utf8());
+ for (const String &arg : p_arguments) {
+ cs.push_back(arg.utf8());
}
Vector<char *> args;
@@ -580,8 +689,8 @@ Error OS_Unix::create_process(const String &p_path, const List<String> &p_argume
Vector<CharString> cs;
cs.push_back(p_path.utf8());
- for (int i = 0; i < p_arguments.size(); i++) {
- cs.push_back(p_arguments[i].utf8());
+ for (const String &arg : p_arguments) {
+ cs.push_back(arg.utf8());
}
Vector<char *> args;
@@ -596,6 +705,11 @@ Error OS_Unix::create_process(const String &p_path, const List<String> &p_argume
raise(SIGKILL);
}
+ ProcessInfo pi;
+ process_map_mutex.lock();
+ process_map->insert(pid, pi);
+ process_map_mutex.unlock();
+
if (r_child_id) {
*r_child_id = pid;
}
@@ -618,14 +732,45 @@ int OS_Unix::get_process_id() const {
}
bool OS_Unix::is_process_running(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
+ const ProcessInfo *pi = process_map->getptr(p_pid);
+
+ if (pi && !pi->is_running) {
+ return false;
+ }
+
int status = 0;
if (waitpid(p_pid, &status, WNOHANG) != 0) {
+ if (pi) {
+ pi->is_running = false;
+ pi->exit_code = status;
+ }
return false;
}
return true;
}
+int OS_Unix::get_process_exit_code(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
+ const ProcessInfo *pi = process_map->getptr(p_pid);
+
+ if (pi && !pi->is_running) {
+ return pi->exit_code;
+ }
+
+ int status = 0;
+ if (waitpid(p_pid, &status, WNOHANG) != 0) {
+ status = WIFEXITED(status) ? WEXITSTATUS(status) : status;
+ if (pi) {
+ pi->is_running = false;
+ pi->exit_code = status;
+ }
+ return status;
+ }
+ return -1;
+}
+
String OS_Unix::get_locale() const {
if (!has_environment("LANG")) {
return "en";
@@ -639,7 +784,7 @@ String OS_Unix::get_locale() const {
return locale;
}
-Error OS_Unix::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) {
+Error OS_Unix::open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data) {
String path = p_path;
if (FileAccess::exists(path) && path.is_relative_path()) {
@@ -663,8 +808,8 @@ Error OS_Unix::open_dynamic_library(const String &p_path, void *&p_library_handl
p_library_handle = dlopen(path.utf8().get_data(), GODOT_DLOPEN_MODE);
ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
- if (r_resolved_path != nullptr) {
- *r_resolved_path = path;
+ if (p_data != nullptr && p_data->r_resolved_path != nullptr) {
+ *p_data->r_resolved_path = path;
}
return OK;
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index d3393c98ec..df269a59d3 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -37,6 +37,13 @@
#include "drivers/unix/ip_unix.h"
class OS_Unix : public OS {
+ struct ProcessInfo {
+ mutable bool is_running = true;
+ mutable int exit_code = -1;
+ };
+ HashMap<ProcessID, ProcessInfo> *process_map = nullptr;
+ Mutex process_map_mutex;
+
protected:
// UNIX only handles the core functions.
// inheriting platforms under unix (eg. X11) should handle the rest
@@ -55,7 +62,7 @@ public:
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override;
- virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override;
+ virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data = nullptr) override;
virtual Error close_dynamic_library(void *p_library_handle) override;
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) override;
@@ -76,10 +83,12 @@ public:
virtual Dictionary get_memory_info() 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, bool p_open_console = false) override;
+ virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) 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;
virtual bool is_process_running(const ProcessID &p_pid) const override;
+ virtual int get_process_exit_code(const ProcessID &p_pid) const override;
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
diff --git a/drivers/vulkan/rendering_context_driver_vulkan.cpp b/drivers/vulkan/rendering_context_driver_vulkan.cpp
index 6eb25743f9..7cba820978 100644
--- a/drivers/vulkan/rendering_context_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_context_driver_vulkan.cpp
@@ -502,6 +502,9 @@ Error RenderingContextDriverVulkan::_initialize_devices() {
driver_device.name = String::utf8(props.deviceName);
driver_device.vendor = Vendor(props.vendorID);
driver_device.type = DeviceType(props.deviceType);
+ driver_device.workarounds = Workarounds();
+
+ _check_driver_workarounds(props, driver_device);
uint32_t queue_family_properties_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &queue_family_properties_count, nullptr);
@@ -515,6 +518,31 @@ Error RenderingContextDriverVulkan::_initialize_devices() {
return OK;
}
+void RenderingContextDriverVulkan::_check_driver_workarounds(const VkPhysicalDeviceProperties &p_device_properties, Device &r_device) {
+ // Workaround for the Adreno 6XX family of devices.
+ //
+ // There's a known issue with the Vulkan driver in this family of devices where it'll crash if a dynamic state for drawing is
+ // used in a command buffer before a dispatch call is issued. As both dynamic scissor and viewport are basic requirements for
+ // the engine to not bake this state into the PSO, the only known way to fix this issue is to reset the command buffer entirely.
+ //
+ // As the render graph has no built in limitations of whether it'll issue compute work before anything needs to draw on the
+ // frame, and there's no guarantee that compute work will never be dependent on rasterization in the future, this workaround
+ // will end recording on the current command buffer any time a compute list is encountered after a draw list was executed.
+ // A new command buffer will be created afterwards and the appropriate synchronization primitives will be inserted.
+ //
+ // Executing this workaround has the added cost of synchronization between all the command buffers that are created as well as
+ // all the individual submissions. This performance hit is accepted for the sake of being able to support these devices without
+ // limiting the design of the renderer.
+ //
+ // This bug was fixed in driver version 512.503.0, so we only enabled it on devices older than this.
+ //
+ r_device.workarounds.avoid_compute_after_draw =
+ r_device.vendor == VENDOR_QUALCOMM &&
+ p_device_properties.deviceID >= 0x6000000 && // Adreno 6xx
+ p_device_properties.driverVersion < VK_MAKE_VERSION(512, 503, 0) &&
+ r_device.name.find("Turnip") < 0;
+}
+
bool RenderingContextDriverVulkan::_use_validation_layers() const {
return Engine::get_singleton()->is_validation_layers_enabled();
}
diff --git a/drivers/vulkan/rendering_context_driver_vulkan.h b/drivers/vulkan/rendering_context_driver_vulkan.h
index 6348f90d55..f1d4021e32 100644
--- a/drivers/vulkan/rendering_context_driver_vulkan.h
+++ b/drivers/vulkan/rendering_context_driver_vulkan.h
@@ -105,6 +105,7 @@ private:
Error _initialize_instance_extensions();
Error _initialize_instance();
Error _initialize_devices();
+ void _check_driver_workarounds(const VkPhysicalDeviceProperties &p_device_properties, Device &r_device);
// Static callbacks.
static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT p_message_severity, VkDebugUtilsMessageTypeFlagsEXT p_message_type, const VkDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index f7d3782d75..896fc6ff91 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -758,11 +758,13 @@ Error RenderingDeviceDriverVulkan::_check_device_capabilities() {
vrs_capabilities.min_texel_size.y = vrs_properties.minFragmentShadingRateAttachmentTexelSize.height;
vrs_capabilities.max_texel_size.x = vrs_properties.maxFragmentShadingRateAttachmentTexelSize.width;
vrs_capabilities.max_texel_size.y = vrs_properties.maxFragmentShadingRateAttachmentTexelSize.height;
+ vrs_capabilities.max_fragment_size.x = vrs_properties.maxFragmentSize.width; // either 4 or 8
+ vrs_capabilities.max_fragment_size.y = vrs_properties.maxFragmentSize.height; // generally the same as width
// We'll attempt to default to a texel size of 16x16.
vrs_capabilities.texel_size = Vector2i(16, 16).clamp(vrs_capabilities.min_texel_size, vrs_capabilities.max_texel_size);
- print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")"));
+ print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")") + String(", max fragment size: (") + itos(vrs_capabilities.max_fragment_size.x) + String(", ") + itos(vrs_capabilities.max_fragment_size.y) + String(")"));
}
} else {
@@ -1008,7 +1010,7 @@ VkResult RenderingDeviceDriverVulkan::_create_render_pass(VkDevice p_device, con
const uint32_t depth_attachment_index = vector_base_index + 3;
_convert_subpass_attachments(p_create_info->pSubpasses[i].pInputAttachments, p_create_info->pSubpasses[i].inputAttachmentCount, subpasses_attachments[input_attachments_index]);
_convert_subpass_attachments(p_create_info->pSubpasses[i].pColorAttachments, p_create_info->pSubpasses[i].colorAttachmentCount, subpasses_attachments[color_attachments_index]);
- _convert_subpass_attachments(p_create_info->pSubpasses[i].pResolveAttachments, p_create_info->pSubpasses[i].colorAttachmentCount, subpasses_attachments[resolve_attachments_index]);
+ _convert_subpass_attachments(p_create_info->pSubpasses[i].pResolveAttachments, (p_create_info->pSubpasses[i].pResolveAttachments != nullptr) ? p_create_info->pSubpasses[i].colorAttachmentCount : 0, subpasses_attachments[resolve_attachments_index]);
_convert_subpass_attachments(p_create_info->pSubpasses[i].pDepthStencilAttachment, (p_create_info->pSubpasses[i].pDepthStencilAttachment != nullptr) ? 1 : 0, subpasses_attachments[depth_attachment_index]);
// Ignores sType and pNext from the subpass.
@@ -2601,7 +2603,7 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
break;
}
- bool present_mode_available = present_modes.find(present_mode) >= 0;
+ bool present_mode_available = present_modes.has(present_mode);
if (present_mode_available) {
print_verbose("Using present mode: " + present_mode_name);
} else {
@@ -4887,6 +4889,10 @@ uint64_t RenderingDeviceDriverVulkan::limit_get(Limit p_limit) {
return vrs_capabilities.texel_size.x;
case LIMIT_VRS_TEXEL_HEIGHT:
return vrs_capabilities.texel_size.y;
+ case LIMIT_VRS_MAX_FRAGMENT_WIDTH:
+ return vrs_capabilities.max_fragment_size.x;
+ case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
+ return vrs_capabilities.max_fragment_size.y;
default:
ERR_FAIL_V(0);
}
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h
index 70c4cebba5..e70019962a 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.h
+++ b/drivers/vulkan/rendering_device_driver_vulkan.h
@@ -87,6 +87,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
Size2i min_texel_size;
Size2i max_texel_size;
+ Size2i max_fragment_size;
Size2i texel_size; // The texel size we'll use
};
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index 8ea1f52d15..a349a66f75 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -107,12 +107,6 @@ const IID IID_IAudioClient3 = __uuidof(IAudioClient3);
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
-#define SAFE_RELEASE(memory) \
- if ((memory) != nullptr) { \
- (memory)->Release(); \
- (memory) = nullptr; \
- }
-
#define REFTIMES_PER_SEC 10000000
#define REFTIMES_PER_MILLISEC 10000
@@ -129,16 +123,10 @@ static bool default_input_device_changed = false;
class CMMNotificationClient : public IMMNotificationClient {
LONG _cRef = 1;
- IMMDeviceEnumerator *_pEnumerator = nullptr;
public:
CMMNotificationClient() {}
- virtual ~CMMNotificationClient() {
- if ((_pEnumerator) != nullptr) {
- (_pEnumerator)->Release();
- (_pEnumerator) = nullptr;
- }
- }
+ virtual ~CMMNotificationClient() {}
ULONG STDMETHODCALLTYPE AddRef() {
return InterlockedIncrement(&_cRef);
@@ -203,8 +191,8 @@ static CMMNotificationClient notif_client;
Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3) {
WAVEFORMATEX *pwfex;
- IMMDeviceEnumerator *enumerator = nullptr;
- IMMDevice *output_device = nullptr;
+ ComPtr<IMMDeviceEnumerator> enumerator = nullptr;
+ ComPtr<IMMDevice> output_device = nullptr;
HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
@@ -212,7 +200,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
if (p_device->device_name == "Default") {
hr = enumerator->GetDefaultAudioEndpoint(p_input ? eCapture : eRender, eConsole, &output_device);
} else {
- IMMDeviceCollection *devices = nullptr;
+ ComPtr<IMMDeviceCollection> devices = nullptr;
hr = enumerator->EnumAudioEndpoints(p_input ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
@@ -225,12 +213,12 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
for (ULONG i = 0; i < count && !found; i++) {
- IMMDevice *tmp_device = nullptr;
+ ComPtr<IMMDevice> tmp_device = nullptr;
hr = devices->Item(i, &tmp_device);
ERR_BREAK(hr != S_OK);
- IPropertyStore *props = nullptr;
+ ComPtr<IPropertyStore> props = nullptr;
hr = tmp_device->OpenPropertyStore(STGM_READ, &props);
ERR_BREAK(hr != S_OK);
@@ -248,8 +236,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
}
PropVariantClear(&propvar);
- props->Release();
- tmp_device->Release();
}
if (found) {
@@ -276,7 +262,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
}
hr = enumerator->RegisterEndpointNotificationCallback(&notif_client);
- SAFE_RELEASE(enumerator)
if (hr != S_OK) {
ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error");
@@ -303,8 +288,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
hr = output_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client);
}
- SAFE_RELEASE(output_device)
-
if (p_reinit) {
if (hr != S_OK) {
return ERR_CANT_OPEN;
@@ -319,7 +302,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
audioProps.bIsOffload = FALSE;
audioProps.eCategory = AudioCategory_GameEffects;
- hr = ((IAudioClient3 *)p_device->audio_client)->SetClientProperties(&audioProps);
+ hr = ((IAudioClient3 *)p_device->audio_client.Get())->SetClientProperties(&audioProps);
ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: SetClientProperties failed with error 0x" + String::num_uint64(hr, 16) + ".");
}
@@ -402,7 +385,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
}
} else {
- IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client;
+ IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client.Get();
// AUDCLNT_STREAMFLAGS_RATEADJUST is an invalid flag with IAudioClient3, therefore we have to use
// the closest supported mix rate supported by the audio driver.
@@ -419,7 +402,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
if (hr != S_OK) {
print_verbose("WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient.");
CoTaskMemFree(pwfex);
- SAFE_RELEASE(output_device)
return audio_device_init(p_device, p_input, p_reinit, true);
}
@@ -441,7 +423,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
if (hr != S_OK) {
print_verbose("WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient.");
CoTaskMemFree(pwfex);
- SAFE_RELEASE(output_device);
return audio_device_init(p_device, p_input, p_reinit, true);
} else {
uint32_t output_latency_in_frames;
@@ -453,7 +434,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
} else {
print_verbose("WASAPI: GetCurrentSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient.");
CoTaskMemFree(pwfex);
- SAFE_RELEASE(output_device);
return audio_device_init(p_device, p_input, p_reinit, true);
}
}
@@ -468,7 +448,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
// Free memory
CoTaskMemFree(pwfex);
- SAFE_RELEASE(output_device)
return OK;
}
@@ -537,9 +516,9 @@ Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) {
p_device->active.clear();
}
- SAFE_RELEASE(p_device->audio_client)
- SAFE_RELEASE(p_device->render_client)
- SAFE_RELEASE(p_device->capture_client)
+ p_device->audio_client.Reset();
+ p_device->render_client.Reset();
+ p_device->capture_client.Reset();
return OK;
}
@@ -581,8 +560,8 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const {
PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) {
PackedStringArray list;
- IMMDeviceCollection *devices = nullptr;
- IMMDeviceEnumerator *enumerator = nullptr;
+ ComPtr<IMMDeviceCollection> devices = nullptr;
+ ComPtr<IMMDeviceEnumerator> enumerator = nullptr;
list.push_back(String("Default"));
@@ -597,12 +576,12 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) {
ERR_FAIL_COND_V(hr != S_OK, PackedStringArray());
for (ULONG i = 0; i < count; i++) {
- IMMDevice *output_device = nullptr;
+ ComPtr<IMMDevice> output_device = nullptr;
hr = devices->Item(i, &output_device);
ERR_BREAK(hr != S_OK);
- IPropertyStore *props = nullptr;
+ ComPtr<IPropertyStore> props = nullptr;
hr = output_device->OpenPropertyStore(STGM_READ, &props);
ERR_BREAK(hr != S_OK);
@@ -615,12 +594,8 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) {
list.push_back(String(propvar.pwszVal));
PropVariantClear(&propvar);
- props->Release();
- output_device->Release();
}
- devices->Release();
- enumerator->Release();
return list;
}
diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h
index 367c30607a..d73cbf4a8a 100644
--- a/drivers/wasapi/audio_driver_wasapi.h
+++ b/drivers/wasapi/audio_driver_wasapi.h
@@ -40,15 +40,18 @@
#include <audioclient.h>
#include <mmdeviceapi.h>
+#include <wrl/client.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
+using Microsoft::WRL::ComPtr;
+
class AudioDriverWASAPI : public AudioDriver {
class AudioDeviceWASAPI {
public:
- IAudioClient *audio_client = nullptr;
- IAudioRenderClient *render_client = nullptr; // Output
- IAudioCaptureClient *capture_client = nullptr; // Input
+ ComPtr<IAudioClient> audio_client = nullptr;
+ ComPtr<IAudioRenderClient> render_client = nullptr; // Output
+ ComPtr<IAudioCaptureClient> capture_client = nullptr; // Input
SafeFlag active;
WORD format_tag = 0;
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 43dd62cdf6..63ba6a6c96 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -396,6 +396,66 @@ bool DirAccessWindows::is_case_sensitive(const String &p_path) const {
}
}
+bool DirAccessWindows::is_link(String p_file) {
+ String f = p_file;
+
+ if (!f.is_absolute_path()) {
+ f = get_current_dir().path_join(f);
+ }
+ f = fix_path(f);
+
+ DWORD attr = GetFileAttributesW((LPCWSTR)(f.utf16().get_data()));
+ if (attr == INVALID_FILE_ATTRIBUTES) {
+ return false;
+ }
+
+ return (attr & FILE_ATTRIBUTE_REPARSE_POINT);
+}
+
+String DirAccessWindows::read_link(String p_file) {
+ String f = p_file;
+
+ if (!f.is_absolute_path()) {
+ f = get_current_dir().path_join(f);
+ }
+ f = fix_path(f);
+
+ HANDLE hfile = CreateFileW((LPCWSTR)(f.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ if (hfile == INVALID_HANDLE_VALUE) {
+ return f;
+ }
+
+ DWORD ret = GetFinalPathNameByHandleW(hfile, nullptr, 0, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED);
+ if (ret == 0) {
+ return f;
+ }
+ Char16String cs;
+ cs.resize(ret + 1);
+ GetFinalPathNameByHandleW(hfile, (LPWSTR)cs.ptrw(), ret, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED);
+ CloseHandle(hfile);
+
+ return String::utf16((const char16_t *)cs.ptr(), ret).trim_prefix(R"(\\?\)");
+}
+
+Error DirAccessWindows::create_link(String p_source, String p_target) {
+ if (p_target.is_relative_path()) {
+ p_target = get_current_dir().path_join(p_target);
+ }
+
+ p_source = fix_path(p_source);
+ p_target = fix_path(p_target);
+
+ DWORD file_attr = GetFileAttributesW((LPCWSTR)(p_source.utf16().get_data()));
+ bool is_dir = (file_attr & FILE_ATTRIBUTE_DIRECTORY);
+
+ DWORD flags = ((is_dir) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+ if (CreateSymbolicLinkW((LPCWSTR)p_target.utf16().get_data(), (LPCWSTR)p_source.utf16().get_data(), flags) != 0) {
+ return OK;
+ } else {
+ return FAILED;
+ }
+}
+
DirAccessWindows::DirAccessWindows() {
p = memnew(DirAccessWindowsPrivate);
p->h = INVALID_HANDLE_VALUE;
diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h
index 576ba18d9a..46755cbf33 100644
--- a/drivers/windows/dir_access_windows.h
+++ b/drivers/windows/dir_access_windows.h
@@ -77,9 +77,9 @@ public:
virtual Error rename(String p_path, String p_new_path) override;
virtual Error remove(String p_path) override;
- virtual bool is_link(String p_file) override { return false; };
- virtual String read_link(String p_file) override { return p_file; };
- virtual Error create_link(String p_source, String p_target) override { return FAILED; };
+ virtual bool is_link(String p_file) override;
+ virtual String read_link(String p_file) override;
+ virtual Error create_link(String p_source, String p_target) override;
uint64_t get_space_left() override;
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index dd8bceb573..9885d9d7ee 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -32,6 +32,7 @@
#include "file_access_windows.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
@@ -41,6 +42,7 @@
#include <windows.h>
#include <errno.h>
+#include <io.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tchar.h>
@@ -120,19 +122,63 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
// Windows is case insensitive, but all other platforms are sensitive to it
// To ease cross-platform development, we issue a warning if users try to access
// a file using the wrong case (which *works* on Windows, but won't on other
- // platforms).
- if (p_mode_flags == READ) {
- WIN32_FIND_DATAW d;
- HANDLE fnd = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d);
- if (fnd != INVALID_HANDLE_VALUE) {
- String fname = String::utf16((const char16_t *)(d.cFileName));
- 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.");
+ // platforms), we only check for relative paths, or paths in res:// or user://,
+ // other paths aren't likely to be portable anyway.
+ if (p_mode_flags == READ && (p_path.is_relative_path() || get_access_type() != ACCESS_FILESYSTEM)) {
+ String base_path = path;
+ String working_path;
+ String proper_path;
+
+ if (get_access_type() == ACCESS_RESOURCES) {
+ if (ProjectSettings::get_singleton()) {
+ working_path = ProjectSettings::get_singleton()->get_resource_path();
+ if (!working_path.is_empty()) {
+ base_path = working_path.path_to_file(base_path);
}
}
+ proper_path = "res://";
+ } else if (get_access_type() == ACCESS_USERDATA) {
+ working_path = OS::get_singleton()->get_user_data_dir();
+ if (!working_path.is_empty()) {
+ base_path = working_path.path_to_file(base_path);
+ }
+ proper_path = "user://";
+ }
+
+ WIN32_FIND_DATAW d;
+ Vector<String> parts = base_path.split("/");
+
+ bool mismatch = false;
+
+ for (const String &part : parts) {
+ working_path = working_path.path_join(part);
+
+ // Skip if relative.
+ if (part == "." || part == "..") {
+ proper_path = proper_path.path_join(part);
+ continue;
+ }
+
+ HANDLE fnd = FindFirstFileW((LPCWSTR)(working_path.utf16().get_data()), &d);
+
+ if (fnd == INVALID_HANDLE_VALUE) {
+ mismatch = false;
+ break;
+ }
+
+ const String fname = String::utf16((const char16_t *)(d.cFileName));
+
FindClose(fnd);
+
+ if (!mismatch) {
+ mismatch = (part != fname && part.findn(fname) == 0);
+ }
+
+ proper_path = proper_path.path_join(fname);
+ }
+
+ if (mismatch) {
+ WARN_PRINT("Case mismatch opening requested file '" + p_path + "', stored as '" + proper_path + "' in the filesystem. This file will not open when exported to other case-sensitive platforms.");
}
}
#endif
@@ -369,6 +415,24 @@ Error FileAccessWindows::get_error() const {
return last_error;
}
+Error FileAccessWindows::resize(int64_t p_length) {
+ ERR_FAIL_NULL_V_MSG(f, FAILED, "File must be opened before use.");
+ errno_t res = _chsize_s(_fileno(f), p_length);
+ switch (res) {
+ case 0:
+ return OK;
+ case EACCES:
+ case EBADF:
+ return ERR_FILE_CANT_OPEN;
+ case ENOSPC:
+ return ERR_OUT_OF_MEMORY;
+ case EINVAL:
+ return ERR_INVALID_PARAMETER;
+ default:
+ return FAILED;
+ }
+}
+
void FileAccessWindows::flush() {
ERR_FAIL_NULL(f);
diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h
index 173423fb06..a25bbcfb3a 100644
--- a/drivers/windows/file_access_windows.h
+++ b/drivers/windows/file_access_windows.h
@@ -77,6 +77,7 @@ public:
virtual Error get_error() const override; ///< get last error
+ virtual Error resize(int64_t p_length) override;
virtual void flush() override;
virtual void store_8(uint8_t p_dest) override; ///< store a byte
virtual void store_16(uint16_t p_dest) override;
diff --git a/drivers/windows/file_access_windows_pipe.cpp b/drivers/windows/file_access_windows_pipe.cpp
new file mode 100644
index 0000000000..7902c8e1d8
--- /dev/null
+++ b/drivers/windows/file_access_windows_pipe.cpp
@@ -0,0 +1,159 @@
+/**************************************************************************/
+/* file_access_windows_pipe.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifdef WINDOWS_ENABLED
+
+#include "file_access_windows_pipe.h"
+
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+
+Error FileAccessWindowsPipe::open_existing(HANDLE p_rfd, HANDLE p_wfd) {
+ // Open pipe using handles created by CreatePipe(rfd, wfd, NULL, 4096) call in the OS.execute_with_pipe.
+ _close();
+
+ path_src = String();
+ ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+ fd[0] = p_rfd;
+ fd[1] = p_wfd;
+
+ last_error = OK;
+ return OK;
+}
+
+Error FileAccessWindowsPipe::open_internal(const String &p_path, int p_mode_flags) {
+ _close();
+
+ path_src = p_path;
+ ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+
+ path = String("\\\\.\\pipe\\LOCAL\\") + p_path.replace("pipe://", "").replace("/", "_");
+
+ HANDLE h = CreateFileW((LPCWSTR)path.utf16().get_data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ h = CreateNamedPipeW((LPCWSTR)path.utf16().get_data(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, nullptr);
+ if (h == INVALID_HANDLE_VALUE) {
+ last_error = ERR_FILE_CANT_OPEN;
+ return last_error;
+ }
+ ConnectNamedPipe(h, NULL);
+ }
+ fd[0] = h;
+ fd[1] = h;
+
+ last_error = OK;
+ return OK;
+}
+
+void FileAccessWindowsPipe::_close() {
+ if (fd[0] == 0) {
+ return;
+ }
+ if (fd[1] != fd[0]) {
+ CloseHandle(fd[1]);
+ }
+ CloseHandle(fd[0]);
+ fd[0] = 0;
+ fd[1] = 0;
+}
+
+bool FileAccessWindowsPipe::is_open() const {
+ return (fd[0] != 0 || fd[1] != 0);
+}
+
+String FileAccessWindowsPipe::get_path() const {
+ return path_src;
+}
+
+String FileAccessWindowsPipe::get_path_absolute() const {
+ return path_src;
+}
+
+uint8_t FileAccessWindowsPipe::get_8() const {
+ ERR_FAIL_COND_V_MSG(fd[0] == 0, 0, "Pipe must be opened before use.");
+
+ uint8_t b;
+ if (!ReadFile(fd[0], &b, 1, nullptr, nullptr)) {
+ last_error = ERR_FILE_CANT_READ;
+ b = '\0';
+ } else {
+ last_error = OK;
+ }
+ return b;
+}
+
+uint64_t FileAccessWindowsPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
+ ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
+ ERR_FAIL_COND_V_MSG(fd[0] == 0, -1, "Pipe must be opened before use.");
+
+ DWORD read = -1;
+ if (!ReadFile(fd[0], p_dst, p_length, &read, nullptr) || read != p_length) {
+ last_error = ERR_FILE_CANT_READ;
+ } else {
+ last_error = OK;
+ }
+ return read;
+}
+
+Error FileAccessWindowsPipe::get_error() const {
+ return last_error;
+}
+
+void FileAccessWindowsPipe::store_8(uint8_t p_src) {
+ ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
+ if (!WriteFile(fd[1], &p_src, 1, nullptr, nullptr)) {
+ last_error = ERR_FILE_CANT_WRITE;
+ } else {
+ last_error = OK;
+ }
+}
+
+void FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
+ ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
+ ERR_FAIL_COND(!p_src && p_length > 0);
+
+ DWORD read = -1;
+ bool ok = WriteFile(fd[1], p_src, p_length, &read, nullptr);
+ if (!ok || read != p_length) {
+ last_error = ERR_FILE_CANT_WRITE;
+ } else {
+ last_error = OK;
+ }
+}
+
+void FileAccessWindowsPipe::close() {
+ _close();
+}
+
+FileAccessWindowsPipe::~FileAccessWindowsPipe() {
+ _close();
+}
+
+#endif // WINDOWS_ENABLED
diff --git a/drivers/windows/file_access_windows_pipe.h b/drivers/windows/file_access_windows_pipe.h
new file mode 100644
index 0000000000..b885ef78e6
--- /dev/null
+++ b/drivers/windows/file_access_windows_pipe.h
@@ -0,0 +1,96 @@
+/**************************************************************************/
+/* file_access_windows_pipe.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef FILE_ACCESS_WINDOWS_PIPE_H
+#define FILE_ACCESS_WINDOWS_PIPE_H
+
+#ifdef WINDOWS_ENABLED
+
+#include "core/io/file_access.h"
+#include "core/os/memory.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+class FileAccessWindowsPipe : public FileAccess {
+ HANDLE fd[2] = { 0, 0 };
+
+ mutable Error last_error = OK;
+
+ String path;
+ String path_src;
+
+ void _close();
+
+public:
+ Error open_existing(HANDLE p_rfd, HANDLE p_wfd);
+
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
+ virtual bool is_open() const override; ///< true when file is open
+
+ virtual String get_path() const override; /// returns the path for the current open file
+ virtual String get_path_absolute() const override; /// returns the absolute path for the current open file
+
+ virtual void seek(uint64_t p_position) override {}
+ virtual void seek_end(int64_t p_position = 0) override {}
+ virtual uint64_t get_position() const override { return 0; }
+ virtual uint64_t get_length() const override { return 0; }
+
+ virtual bool eof_reached() const override { return false; }
+
+ virtual uint8_t get_8() const override; ///< get a byte
+ virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
+
+ virtual Error get_error() const override; ///< get last error
+
+ virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
+ virtual void flush() override {}
+ virtual void store_8(uint8_t p_src) override; ///< store a byte
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
+
+ virtual bool file_exists(const String &p_name) override { return false; }
+
+ uint64_t _get_modified_time(const String &p_file) override { return 0; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
+
+ virtual void close() override;
+
+ FileAccessWindowsPipe() {}
+ virtual ~FileAccessWindowsPipe();
+};
+
+#endif // WINDOWS_ENABLED
+
+#endif // FILE_ACCESS_WINDOWS_PIPE_H