diff options
Diffstat (limited to 'drivers')
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 ¤t_display = displays[current_window->gldisplay_id]; - - eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context); -} - void EGLManager::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(¬if_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 |