diff options
Diffstat (limited to 'drivers')
37 files changed, 784 insertions, 325 deletions
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index 689f76389b..966137920a 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -44,7 +44,8 @@ extern int initialize_pulse(int verbose); #endif Error AudioDriverALSA::init_output_device() { - mix_rate = GLOBAL_GET("audio/driver/mix_rate"); + mix_rate = _get_configured_mix_rate(); + speaker_mode = SPEAKER_MODE_STEREO; channels = 2; @@ -169,6 +170,18 @@ Error AudioDriverALSA::init() { return ERR_CANT_OPEN; } #endif + bool ver_ok = false; + String version = String::utf8(snd_asoundlib_version()); + Vector<String> ver_parts = version.split("."); + if (ver_parts.size() >= 2) { + ver_ok = ((ver_parts[0].to_int() == 1 && ver_parts[1].to_int() >= 1)) || (ver_parts[0].to_int() > 1); // 1.1.0 + } + print_verbose(vformat("ALSA %s detected.", version)); + if (!ver_ok) { + print_verbose("Unsupported ALSA library version!"); + return ERR_CANT_OPEN; + } + active.clear(); exit_thread.clear(); @@ -326,7 +339,9 @@ void AudioDriverALSA::finish_output_device() { void AudioDriverALSA::finish() { exit_thread.set(); - thread.wait_to_finish(); + if (thread.is_started()) { + thread.wait_to_finish(); + } finish_output_device(); } diff --git a/drivers/alsamidi/midi_driver_alsamidi.cpp b/drivers/alsamidi/midi_driver_alsamidi.cpp index 81472fe70c..6b35987f70 100644 --- a/drivers/alsamidi/midi_driver_alsamidi.cpp +++ b/drivers/alsamidi/midi_driver_alsamidi.cpp @@ -207,7 +207,9 @@ Error MIDIDriverALSAMidi::open() { void MIDIDriverALSAMidi::close() { exit_thread.set(); - thread.wait_to_finish(); + if (thread.is_started()) { + thread.wait_to_finish(); + } for (int i = 0; i < connected_inputs.size(); i++) { snd_rawmidi_t *midi_in = connected_inputs[i].rawmidi_ptr; diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index 2c959bb07b..4011727433 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -116,7 +116,7 @@ Error AudioDriverCoreAudio::init() { break; } - mix_rate = GLOBAL_GET("audio/driver/mix_rate"); + mix_rate = _get_configured_mix_rate(); memset(&strdesc, 0, sizeof(strdesc)); strdesc.mFormatID = kAudioFormatLinearPCM; @@ -405,7 +405,7 @@ Error AudioDriverCoreAudio::init_input_device() { break; } - mix_rate = GLOBAL_GET("audio/driver/mix_rate"); + mix_rate = _get_configured_mix_rate(); memset(&strdesc, 0, sizeof(strdesc)); strdesc.mFormatID = kAudioFormatLinearPCM; diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub index 2204c486c6..91240ce3e3 100644 --- a/drivers/gl_context/SCsub +++ b/drivers/gl_context/SCsub @@ -10,7 +10,11 @@ if env["platform"] in ["haiku", "macos", "windows", "linuxbsd"]: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env.Prepend(CPPPATH=[thirdparty_dir]) + # Treat glad headers as system headers to avoid raising warnings. Not supported on MSVC. + if not env.msvc: + env.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path]) + else: + env.Prepend(CPPPATH=[thirdparty_dir]) env.Append(CPPDEFINES=["GLAD_ENABLED"]) env.Append(CPPDEFINES=["GLES_OVER_GL"]) diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h index 38f79b96a6..105fbcfe98 100644 --- a/drivers/gles3/effects/copy_effects.h +++ b/drivers/gles3/effects/copy_effects.h @@ -33,7 +33,7 @@ #ifdef GLES3_ENABLED -#include "../shaders/copy.glsl.gen.h" +#include "drivers/gles3/shaders/copy.glsl.gen.h" namespace GLES3 { diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 522685bf87..19f2cfd47d 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1310,6 +1310,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { uint32_t instance_color_offset = 0; bool instance_uses_color = false; bool instance_uses_custom_data = false; + bool use_instancing = false; if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_MESH) { const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(state.canvas_instance_batches[p_index].command); @@ -1336,6 +1337,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { instance_color_offset = mesh_storage->multimesh_get_color_offset(multimesh); instance_uses_color = mesh_storage->multimesh_uses_colors(multimesh); instance_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh); + use_instancing = true; } else if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_PARTICLES) { const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(state.canvas_instance_batches[p_index].command); @@ -1362,6 +1364,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { instance_color_offset = 8; // 8 bytes for instance transform. instance_uses_color = true; instance_uses_custom_data = true; + use_instancing = true; } ERR_FAIL_COND(mesh.is_null()); @@ -1397,7 +1400,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { use_index_buffer = true; } - if (instance_count > 1) { + if (use_instancing) { if (instance_buffer == 0) { break; } @@ -1426,7 +1429,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { } glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - if (instance_count > 1) { + if (use_instancing) { glDisableVertexAttribArray(5); glDisableVertexAttribArray(6); glDisableVertexAttribArray(7); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 1c14d0b466..2eac5f57e1 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -39,8 +39,8 @@ #include "storage/material_storage.h" #include "storage/texture_storage.h" -#include "shaders/canvas.glsl.gen.h" -#include "shaders/canvas_occlusion.glsl.gen.h" +#include "drivers/gles3/shaders/canvas.glsl.gen.h" +#include "drivers/gles3/shaders/canvas_occlusion.glsl.gen.h" class RasterizerSceneGLES3; diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 7cbce428cb..6748e93383 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -34,6 +34,7 @@ #ifdef GLES3_ENABLED #include "core/config/project_settings.h" +#include "core/io/dir_access.h" #include "core/os/os.h" #include "storage/texture_storage.h" @@ -257,6 +258,36 @@ RasterizerGLES3::RasterizerGLES3() { #endif // GLES_OVER_GL #endif // CAN_DEBUG + { + String shader_cache_dir = Engine::get_singleton()->get_shader_cache_path(); + if (shader_cache_dir.is_empty()) { + shader_cache_dir = "user://"; + } + Ref<DirAccess> da = DirAccess::open(shader_cache_dir); + if (da.is_null()) { + ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir); + } else { + Error err = da->change_dir("shader_cache"); + if (err != OK) { + err = da->make_dir("shader_cache"); + } + if (err != OK) { + ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir); + } else { + shader_cache_dir = shader_cache_dir.path_join("shader_cache"); + + bool shader_cache_enabled = GLOBAL_GET("rendering/shader_compiler/shader_cache/enabled"); + if (!Engine::get_singleton()->is_editor_hint() && !shader_cache_enabled) { + shader_cache_dir = String(); //disable only if not editor + } + + if (!shader_cache_dir.is_empty()) { + ShaderGLES3::set_shader_cache_dir(shader_cache_dir); + } + } + } + } + // OpenGL needs to be initialized before initializing the Rasterizers config = memnew(GLES3::Config); utilities = memnew(GLES3::Utilities); @@ -300,12 +331,13 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display } GLuint read_fbo = 0; + glGenFramebuffers(1, &read_fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo); + if (rt->view_count > 1) { - glGenFramebuffers(1, &read_fbo); - glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo); glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, p_layer); } else { - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); } glReadBuffer(GL_COLOR_ATTACHMENT0); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 9a4deefdc8..3d8f7924a7 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1948,7 +1948,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ glDepthFunc(GL_LEQUAL); glDepthMask(GL_TRUE); scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; - scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE; + scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS; if (!fb_cleared) { glClearDepth(1.0f); @@ -1976,24 +1976,44 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ _render_list_template<PASS_MODE_COLOR>(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); + glDepthMask(GL_FALSE); + scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_DISABLED; + if (draw_sky) { RENDER_TIMESTAMP("Render Sky"); - if (scene_state.current_depth_test != GLES3::SceneShaderData::DEPTH_TEST_ENABLED) { - glEnable(GL_DEPTH_TEST); - scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; - } + glEnable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); glDisable(GL_BLEND); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; - scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_DISABLED; scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform, sky_energy_multiplier, p_camera_data->view_count > 1, flip_y); } + if (scene_state.used_screen_texture || scene_state.used_depth_texture) { + texture_storage->copy_scene_to_backbuffer(rt, scene_state.used_screen_texture, scene_state.used_depth_texture); + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rt->backbuffer_fbo); + if (scene_state.used_screen_texture) { + glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, + 0, 0, rt->size.x, rt->size.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); + glBindTexture(GL_TEXTURE_2D, rt->backbuffer); + } + if (scene_state.used_depth_texture) { + glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, + 0, 0, rt->size.x, rt->size.y, + GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_2D, rt->backbuffer_depth); + } + glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); + } + RENDER_TIMESTAMP("Render 3D Transparent Pass"); glEnable(GL_BLEND); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index ff043d67f6..b4e787ad85 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -37,13 +37,13 @@ #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/sky.glsl.gen.h" #include "scene/resources/mesh.h" #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/renderer_scene_render.h" #include "servers/rendering_server.h" #include "shader_gles3.h" -#include "shaders/cubemap_filter.glsl.gen.h" -#include "shaders/sky.glsl.gen.h" #include "storage/light_storage.h" #include "storage/material_storage.h" #include "storage/render_scene_buffers_gles3.h" diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 10f42bf22b..ded5793e8f 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -242,6 +242,41 @@ static void _display_error_with_code(const String &p_error, const String &p_code ERR_PRINT(p_error); } +void ShaderGLES3::_get_uniform_locations(Version::Specialization &spec, Version *p_version) { + glUseProgram(spec.id); + + spec.uniform_location.resize(uniform_count); + for (int i = 0; i < uniform_count; i++) { + spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]); + } + + for (int i = 0; i < texunit_pair_count; i++) { + GLint loc = glGetUniformLocation(spec.id, texunit_pairs[i].name); + if (loc >= 0) { + if (texunit_pairs[i].index < 0) { + glUniform1i(loc, max_image_units + texunit_pairs[i].index); + } else { + glUniform1i(loc, texunit_pairs[i].index); + } + } + } + + for (int i = 0; i < ubo_count; i++) { + GLint loc = glGetUniformBlockIndex(spec.id, ubo_pairs[i].name); + if (loc >= 0) { + glUniformBlockBinding(spec.id, loc, ubo_pairs[i].index); + } + } + // textures + for (int i = 0; i < p_version->texture_uniforms.size(); i++) { + String native_uniform_name = _mkid(p_version->texture_uniforms[i]); + GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data()); + glUniform1i(location, i + base_texture_index); + } + + glUseProgram(0); +} + void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization) { spec.id = glCreateProgram(); spec.ok = false; @@ -402,40 +437,8 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_ ERR_FAIL(); } - // get uniform locations - - glUseProgram(spec.id); - - spec.uniform_location.resize(uniform_count); - for (int i = 0; i < uniform_count; i++) { - spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]); - } - - for (int i = 0; i < texunit_pair_count; i++) { - GLint loc = glGetUniformLocation(spec.id, texunit_pairs[i].name); - if (loc >= 0) { - if (texunit_pairs[i].index < 0) { - glUniform1i(loc, max_image_units + texunit_pairs[i].index); - } else { - glUniform1i(loc, texunit_pairs[i].index); - } - } - } - - for (int i = 0; i < ubo_count; i++) { - GLint loc = glGetUniformBlockIndex(spec.id, ubo_pairs[i].name); - if (loc >= 0) { - glUniformBlockBinding(spec.id, loc, ubo_pairs[i].index); - } - } - // textures - for (int i = 0; i < p_version->texture_uniforms.size(); i++) { - String native_uniform_name = _mkid(p_version->texture_uniforms[i]); - GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data()); - glUniform1i(location, i + base_texture_index); - } + _get_uniform_locations(spec, p_version); - glUseProgram(0); spec.ok = true; } @@ -504,11 +507,20 @@ String ShaderGLES3::_version_get_sha1(Version *p_version) const { return hash_build.as_string().sha1_text(); } -//static const char *shader_file_header = "GLSC"; -//static const uint32_t cache_file_version = 2; +#ifndef WEB_ENABLED // not supported in webgl +static const char *shader_file_header = "GLSC"; +static const uint32_t cache_file_version = 3; +#endif bool ShaderGLES3::_load_from_cache(Version *p_version) { -#if 0 +#ifdef WEB_ENABLED // not supported in webgl + return false; +#else +#ifdef GLES_OVER_GL + if (glProgramBinary == NULL) { // ARB_get_program_binary extension not available + return false; + } +#endif String sha1 = _version_get_sha1(p_version); String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache"; @@ -517,7 +529,7 @@ bool ShaderGLES3::_load_from_cache(Version *p_version) { return false; } - char header[5] = { 0, 0, 0, 0, 0 }; + char header[5] = {}; f->get_buffer((uint8_t *)header, 4); ERR_FAIL_COND_V(header != String(shader_file_header), false); @@ -526,70 +538,98 @@ bool ShaderGLES3::_load_from_cache(Version *p_version) { return false; // wrong version } - uint32_t variant_count = f->get_32(); + int cache_variant_count = static_cast<int>(f->get_32()); + ERR_FAIL_COND_V_MSG(cache_variant_count != this->variant_count, false, "shader cache variant count mismatch, expected " + itos(this->variant_count) + " got " + itos(cache_variant_count)); //should not happen but check + + LocalVector<OAHashMap<uint64_t, Version::Specialization>> variants; + for (int i = 0; i < cache_variant_count; i++) { + uint32_t cache_specialization_count = f->get_32(); + OAHashMap<uint64_t, Version::Specialization> variant; + for (uint32_t j = 0; j < cache_specialization_count; j++) { + uint64_t specialization_key = f->get_64(); + uint32_t variant_size = f->get_32(); + if (variant_size == 0) { + continue; + } + uint32_t variant_format = f->get_32(); + Vector<uint8_t> variant_bytes; + variant_bytes.resize(variant_size); + + uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size); - ERR_FAIL_COND_V(variant_count != (uint32_t)variant_count, false); //should not happen but check + ERR_FAIL_COND_V(br != variant_size, false); - for (uint32_t i = 0; i < variant_count; i++) { - uint32_t variant_size = f->get_32(); - ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[i], false); - if (!variants_enabled[i]) { - continue; - } - Vector<uint8_t> variant_bytes; - variant_bytes.resize(variant_size); + Version::Specialization specialization; - uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size); + specialization.id = glCreateProgram(); + glProgramBinary(specialization.id, variant_format, variant_bytes.ptr(), variant_bytes.size()); - ERR_FAIL_COND_V(br != variant_size, false); + _get_uniform_locations(specialization, p_version); - p_version->variant_data[i] = variant_bytes; - } + specialization.ok = true; - for (uint32_t i = 0; i < variant_count; i++) { - if (!variants_enabled[i]) { - MutexLock lock(variant_set_mutex); - p_version->variants[i] = RID(); - continue; - } - RID shader = GLES3::get_singleton()->shader_create_from_bytecode(p_version->variant_data[i]); - if (shader.is_null()) { - for (uint32_t j = 0; j < i; j++) { - GLES3::get_singleton()->free(p_version->variants[i]); - } - ERR_FAIL_COND_V(shader.is_null(), false); - } - { - MutexLock lock(variant_set_mutex); - p_version->variants[i] = shader; + variant.insert(specialization_key, specialization); } + variants.push_back(variant); } + p_version->variants = variants; - memdelete_arr(p_version->variant_data); //clear stages - p_version->variant_data = nullptr; - p_version->valid = true; return true; -#endif - return false; +#endif // WEB_ENABLED } void ShaderGLES3::_save_to_cache(Version *p_version) { -#if 0 +#ifdef WEB_ENABLED // not supported in webgl + return; +#else +#ifdef GLES_OVER_GL + if (glGetProgramBinary == NULL) { // ARB_get_program_binary extension not available + return; + } +#endif String sha1 = _version_get_sha1(p_version); String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache"; - Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE); + Error error; + Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE, &error); ERR_FAIL_COND(f.is_null()); f->store_buffer((const uint8_t *)shader_file_header, 4); - f->store_32(cache_file_version); //file version - uint32_t variant_count = variant_count; - f->store_32(variant_count); //variant count + f->store_32(cache_file_version); + f->store_32(variant_count); + + for (int i = 0; i < variant_count; i++) { + int cache_specialization_count = p_version->variants[i].get_num_elements(); + f->store_32(cache_specialization_count); + + for (OAHashMap<uint64_t, ShaderGLES3::Version::Specialization>::Iterator it = p_version->variants[i].iter(); it.valid; it = p_version->variants[i].next_iter(it)) { + const uint64_t specialization_key = *it.key; + f->store_64(specialization_key); - for (uint32_t i = 0; i < variant_count; i++) { - f->store_32(p_version->variant_data[i].size()); //stage count - f->store_buffer(p_version->variant_data[i].ptr(), p_version->variant_data[i].size()); + const Version::Specialization *specialization = it.value; + if (specialization == nullptr) { + f->store_32(0); + continue; + } + GLint program_size = 0; + glGetProgramiv(specialization->id, GL_PROGRAM_BINARY_LENGTH, &program_size); + if (program_size == 0) { + f->store_32(0); + continue; + } + PackedByteArray compiled_program; + compiled_program.resize(program_size); + GLenum binary_format = 0; + glGetProgramBinary(specialization->id, program_size, nullptr, &binary_format, compiled_program.ptrw()); + if (program_size != compiled_program.size()) { + f->store_32(0); + continue; + } + f->store_32(program_size); + f->store_32(binary_format); + f->store_buffer(compiled_program.ptr(), compiled_program.size()); + } } -#endif +#endif // WEB_ENABLED } void ShaderGLES3::_clear_version(Version *p_version) { @@ -613,6 +653,9 @@ void ShaderGLES3::_clear_version(Version *p_version) { void ShaderGLES3::_initialize_version(Version *p_version) { ERR_FAIL_COND(p_version->variants.size() > 0); + if (_load_from_cache(p_version)) { + return; + } p_version->variants.reserve(variant_count); for (int i = 0; i < variant_count; i++) { OAHashMap<uint64_t, Version::Specialization> variant; @@ -621,6 +664,7 @@ void ShaderGLES3::_initialize_version(Version *p_version) { _compile_specialization(spec, i, p_version, specialization_default_mask); p_version->variants[i].insert(specialization_default_mask, spec); } + _save_to_cache(p_version); } void ShaderGLES3::version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const Vector<StringName> &p_texture_uniforms, bool p_initialize) { diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index 0b2ecbaca6..565434bb36 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -98,7 +98,6 @@ private: GLuint frag_id; LocalVector<GLint> uniform_location; LocalVector<GLint> texture_uniform_locations; - HashMap<StringName, GLint> custom_uniform_locations; bool build_queued = false; bool ok = false; Specialization() { @@ -113,6 +112,7 @@ private: Mutex variant_set_mutex; + void _get_uniform_locations(Version::Specialization &spec, Version *p_version); void _compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization); void _clear_version(Version *p_version); @@ -209,6 +209,7 @@ protected: _compile_specialization(s, p_variant, version, p_specialization); version->variants[p_variant].insert(p_specialization, s); spec = version->variants[p_variant].lookup_ptr(p_specialization); + _save_to_cache(version); } } else if (spec->build_queued) { // Still queued, wait diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index ea0a0b660d..ae3892fb61 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -589,7 +589,7 @@ void main() { if (bool(read_draw_data_flags & FLAGS_FLIP_V)) { normal.y = -normal.y; } - normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); + normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, normal.xy))); normal_used = true; } else { normal = vec3(0.0, 0.0, 1.0); diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl index f8741a22ab..40881a1808 100644 --- a/drivers/gles3/shaders/particles.glsl +++ b/drivers/gles3/shaders/particles.glsl @@ -300,28 +300,23 @@ void main() { vec3 rel_vec = xform[3].xyz - attractors[i].transform[3].xyz; vec3 local_pos = rel_vec * mat3(attractors[i].transform); - switch (attractors[i].type) { - case ATTRACTOR_TYPE_SPHERE: { - dir = safe_normalize(rel_vec); - float d = length(local_pos) / attractors[i].extents.x; - if (d > 1.0) { - continue; - } - amount = max(0.0, 1.0 - d); - } break; - case ATTRACTOR_TYPE_BOX: { - dir = safe_normalize(rel_vec); - - vec3 abs_pos = abs(local_pos / attractors[i].extents.xyz); - float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z)); - if (d > 1.0) { - continue; - } - amount = max(0.0, 1.0 - d); - - } break; - case ATTRACTOR_TYPE_VECTOR_FIELD: { - } break; + if (attractors[i].type == ATTRACTOR_TYPE_SPHERE) { + dir = safe_normalize(rel_vec); + float d = length(local_pos) / attractors[i].extents.x; + if (d > 1.0) { + continue; + } + amount = max(0.0, 1.0 - d); + } else if (attractors[i].type == ATTRACTOR_TYPE_BOX) { + dir = safe_normalize(rel_vec); + + vec3 abs_pos = abs(local_pos / attractors[i].extents.xyz); + float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z)); + if (d > 1.0) { + continue; + } + amount = max(0.0, 1.0 - d); + } else if (attractors[i].type == ATTRACTOR_TYPE_VECTOR_FIELD) { } amount = pow(amount, attractors[i].attenuation); dir = safe_normalize(mix(dir, attractors[i].transform[2].xyz, attractors[i].directionality)); @@ -383,80 +378,72 @@ void main() { vec3 rel_vec = xform[3].xyz - colliders[i].transform[3].xyz; vec3 local_pos = rel_vec * mat3(colliders[i].transform); - switch (colliders[i].type) { - case COLLIDER_TYPE_SPHERE: { - float d = length(rel_vec) - (particle_size + colliders[i].extents.x); + if (colliders[i].type == COLLIDER_TYPE_SPHERE) { + float d = length(rel_vec) - (particle_size + colliders[i].extents.x); - if (d < 0.0) { - col = true; - depth = -d; - normal = normalize(rel_vec); - } + if (d < 0.0) { + col = true; + depth = -d; + normal = normalize(rel_vec); + } + } else if (colliders[i].type == COLLIDER_TYPE_BOX) { + vec3 abs_pos = abs(local_pos); + vec3 sgn_pos = sign(local_pos); - } break; - case COLLIDER_TYPE_BOX: { - vec3 abs_pos = abs(local_pos); - vec3 sgn_pos = sign(local_pos); - - if (any(greaterThan(abs_pos, colliders[i].extents.xyz))) { - //point outside box - - vec3 closest = min(abs_pos, colliders[i].extents.xyz); - vec3 rel = abs_pos - closest; - depth = length(rel) - particle_size; - if (depth < 0.0) { - col = true; - normal = mat3(colliders[i].transform) * (normalize(rel) * sgn_pos); - depth = -depth; - } - } else { - //point inside box - vec3 axis_len = colliders[i].extents.xyz - abs_pos; - // there has to be a faster way to do this? - if (all(lessThan(axis_len.xx, axis_len.yz))) { - normal = vec3(1, 0, 0); - } else if (all(lessThan(axis_len.yy, axis_len.xz))) { - normal = vec3(0, 1, 0); - } else { - normal = vec3(0, 0, 1); - } + if (any(greaterThan(abs_pos, colliders[i].extents.xyz))) { + //point outside box + vec3 closest = min(abs_pos, colliders[i].extents.xyz); + vec3 rel = abs_pos - closest; + depth = length(rel) - particle_size; + if (depth < 0.0) { col = true; - depth = dot(normal * axis_len, vec3(1)) + particle_size; - normal = mat3(colliders[i].transform) * (normal * sgn_pos); + normal = mat3(colliders[i].transform) * (normalize(rel) * sgn_pos); + depth = -depth; } - - } break; - case COLLIDER_TYPE_SDF: { - } break; - case COLLIDER_TYPE_HEIGHT_FIELD: { - vec3 local_pos_bottom = local_pos; - local_pos_bottom.y -= particle_size; - - if (any(greaterThan(abs(local_pos_bottom), colliders[i].extents.xyz))) { - continue; + } else { + //point inside box + vec3 axis_len = colliders[i].extents.xyz - abs_pos; + // there has to be a faster way to do this? + if (all(lessThan(axis_len.xx, axis_len.yz))) { + normal = vec3(1, 0, 0); + } else if (all(lessThan(axis_len.yy, axis_len.xz))) { + normal = vec3(0, 1, 0); + } else { + normal = vec3(0, 0, 1); } - const float DELTA = 1.0 / 8192.0; - vec3 uvw_pos = vec3(local_pos_bottom / colliders[i].extents.xyz) * 0.5 + 0.5; + col = true; + depth = dot(normal * axis_len, vec3(1)) + particle_size; + normal = mat3(colliders[i].transform) * (normal * sgn_pos); + } + } else if (colliders[i].type == COLLIDER_TYPE_SDF) { + } else if (colliders[i].type == COLLIDER_TYPE_HEIGHT_FIELD) { + vec3 local_pos_bottom = local_pos; + local_pos_bottom.y -= particle_size; - float y = 1.0 - texture(height_field_texture, uvw_pos.xz).r; + if (any(greaterThan(abs(local_pos_bottom), colliders[i].extents.xyz))) { + continue; + } + const float DELTA = 1.0 / 8192.0; - if (y > uvw_pos.y) { - //inside heightfield + vec3 uvw_pos = vec3(local_pos_bottom / colliders[i].extents.xyz) * 0.5 + 0.5; - vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz; - vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz; - vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * colliders[i].extents.xyz; + float y = 1.0 - texture(height_field_texture, uvw_pos.xz).r; - normal = normalize(cross(pos1 - pos2, pos1 - pos3)); - float local_y = (vec3(local_pos / colliders[i].extents.xyz) * 0.5 + 0.5).y; + if (y > uvw_pos.y) { + //inside heightfield - col = true; - depth = dot(normal, pos1) - dot(normal, local_pos_bottom); - } + vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz; + vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz; + vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * colliders[i].extents.xyz; - } break; + normal = normalize(cross(pos1 - pos2, pos1 - pos3)); + float local_y = (vec3(local_pos / colliders[i].extents.xyz) * 0.5 + 0.5).y; + + col = true; + depth = dot(normal, pos1) - dot(normal, local_pos_bottom); + } } if (col) { diff --git a/drivers/gles3/shaders/particles_copy.glsl b/drivers/gles3/shaders/particles_copy.glsl index f273cb7b64..774aa139b3 100644 --- a/drivers/gles3/shaders/particles_copy.glsl +++ b/drivers/gles3/shaders/particles_copy.glsl @@ -44,8 +44,12 @@ uniform highp mat4 inv_emission_transform; #define PARTICLE_FLAG_ACTIVE uint(1) +#define FLT_MAX float(3.402823466e+38) + void main() { - mat4 txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); // zero scale, becomes invisible. + // Set scale to zero and translate to -INF so particle will be invisible + // even for materials that ignore rotation/scale (i.e. billboards). + mat4 txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(-FLT_MAX, -FLT_MAX, -FLT_MAX, 0.0)); if (bool(floatBitsToUint(velocity_flags.w) & PARTICLE_FLAG_ACTIVE)) { #ifdef MODE_3D txform = transpose(mat4(xform_1, xform_2, xform_3, vec4(0.0, 0.0, 0.0, 1.0))); @@ -102,9 +106,8 @@ void main() { // as they will be drawn with the node position as origin. txform = inv_emission_transform * txform; #endif - - txform = transpose(txform); } + txform = transpose(txform); instance_color_custom_data = uvec4(packHalf2x16(color.xy), packHalf2x16(color.zw), packHalf2x16(custom.xy), packHalf2x16(custom.zw)); out_xform_1 = txform[0]; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 0b389b0478..37976bb9a0 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -624,7 +624,7 @@ float SchlickFresnel(float u) { return m2 * m2 * m; // pow(m,5) } -void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float attenuation, vec3 f0, float roughness, float metallic, float specular_amount, vec3 albedo, inout float alpha, +void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, vec3 f0, float roughness, float metallic, float specular_amount, vec3 albedo, inout float alpha, #ifdef LIGHT_BACKLIGHT_USED vec3 backlight, #endif @@ -771,7 +771,7 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float atte alpha = min(alpha, clamp(1.0 - attenuation, 0.0, 1.0)); #endif -#endif // LIGHT_CODE_USED +#endif // USE_LIGHT_SHADER_CODE } float get_omni_spot_attenuation(float distance, float inv_range, float decay) { @@ -809,7 +809,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f size_A = max(0.0, 1.0 - 1.0 / sqrt(1.0 + t * t)); } - light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, omni_attenuation, f0, roughness, metallic, omni_lights[idx].specular_amount, albedo, alpha, + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, omni_attenuation, f0, roughness, metallic, omni_lights[idx].specular_amount, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, #endif @@ -860,7 +860,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f size_A = max(0.0, 1.0 - 1.0 / sqrt(1.0 + t * t)); } - light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, spot_attenuation, f0, roughness, metallic, spot_lights[idx].specular_amount, albedo, alpha, + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, spot_attenuation, f0, roughness, metallic, spot_lights[idx].specular_amount, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, #endif @@ -1189,7 +1189,7 @@ void main() { #ifndef DISABLE_LIGHT_DIRECTIONAL //diffuse_light = normal; // for (uint i = uint(0); i < scene_data.directional_light_count; i++) { - light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, 1.0, f0, roughness, metallic, 1.0, albedo, alpha, + light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, true, 1.0, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, #endif diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index 026f7467a8..8da5e657cd 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -76,6 +76,7 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0; light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0; light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05; + light.param[RS::LIGHT_PARAM_INTENSITY] = p_type == RS::LIGHT_DIRECTIONAL ? 100000.0 : 1000.0; light_owner.initialize_rid(p_light, light); } diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index fc6c2f3a26..c7b2a715be 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1489,7 +1489,7 @@ MaterialStorage::MaterialStorage() { global_shader_uniforms.buffer_size = MAX(4096, (int)GLOBAL_GET("rendering/limits/global_shader_variables/buffer_size")); if (global_shader_uniforms.buffer_size > uint32_t(Config::get_singleton()->max_uniform_buffer_size)) { global_shader_uniforms.buffer_size = uint32_t(Config::get_singleton()->max_uniform_buffer_size); - WARN_PRINT("Project setting: rendering/limits/global_shader_variables/buffer_size exceeds maximum uniform buffer size of: " + itos(Config::get_singleton()->max_uniform_buffer_size)); + WARN_PRINT("Project setting \"rendering/limits/global_shader_variables/buffer_size\" exceeds maximum uniform buffer size of: " + itos(Config::get_singleton()->max_uniform_buffer_size)); } global_shader_uniforms.buffer_values = memnew_arr(GlobalShaderUniforms::Value, global_shader_uniforms.buffer_size); @@ -1603,6 +1603,7 @@ MaterialStorage::MaterialStorage() { //builtins actions.renames["TIME"] = "scene_data.time"; + actions.renames["EXPOSURE"] = "(1.0 / scene_data.emissive_exposure_normalization)"; actions.renames["PI"] = _MKSTR(Math_PI); actions.renames["TAU"] = _MKSTR(Math_TAU); actions.renames["E"] = _MKSTR(Math_E); @@ -1650,7 +1651,7 @@ MaterialStorage::MaterialStorage() { actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz"; actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers"; - actions.renames["NODE_POSITION_VIEW"] = "(model_matrix * scene_data.view_matrix)[3].xyz"; + actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * model_matrix)[3].xyz"; actions.renames["VIEW_INDEX"] = "ViewIndex"; actions.renames["VIEW_MONO_LEFT"] = "uint(0)"; @@ -1659,7 +1660,9 @@ MaterialStorage::MaterialStorage() { //for light actions.renames["VIEW"] = "view"; + actions.renames["SPECULAR_AMOUNT"] = "specular_amount"; actions.renames["LIGHT_COLOR"] = "light_color"; + actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional"; actions.renames["LIGHT"] = "light"; actions.renames["ATTENUATION"] = "attenuation"; actions.renames["DIFFUSE_LIGHT"] = "diffuse_light"; diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 4c5861b017..92399127bb 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -42,11 +42,11 @@ #include "servers/rendering/storage/material_storage.h" #include "servers/rendering/storage/utilities.h" -#include "../shaders/canvas.glsl.gen.h" -#include "../shaders/cubemap_filter.glsl.gen.h" -#include "../shaders/particles.glsl.gen.h" -#include "../shaders/scene.glsl.gen.h" -#include "../shaders/sky.glsl.gen.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" namespace GLES3 { diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index e1c2bc3f63..f9122771f9 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -33,10 +33,10 @@ #ifdef GLES3_ENABLED -#include "../shaders/skeleton.glsl.gen.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" +#include "drivers/gles3/shaders/skeleton.glsl.gen.h" #include "servers/rendering/storage/mesh_storage.h" #include "servers/rendering/storage/utilities.h" diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp index 2b47271408..4b64e2aec1 100644 --- a/drivers/gles3/storage/particles_storage.cpp +++ b/drivers/gles3/storage/particles_storage.cpp @@ -919,7 +919,7 @@ void ParticlesStorage::_particles_update_instance_buffer(Particles *particles, c glBeginTransformFeedback(GL_POINTS); if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME) { - uint32_t lifetime_split = MIN(particles->amount * particles->phase, particles->amount - 1); + uint32_t lifetime_split = (MIN(int(particles->amount * particles->phase), particles->amount - 1) + 1) % particles->amount; uint32_t stride = particles->process_buffer_stride_cache; glBindBuffer(GL_ARRAY_BUFFER, particles->back_process_buffer); @@ -1135,14 +1135,13 @@ void ParticlesStorage::_particles_reverse_lifetime_sort(Particles *particles) { glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer_size, particle_array); #endif - uint32_t lifetime_split = MIN(particles->amount * particles->sort_buffer_phase, particles->amount - 1); - + uint32_t lifetime_split = (MIN(int(particles->amount * particles->sort_buffer_phase), particles->amount - 1) + 1) % particles->amount; for (uint32_t i = 0; i < lifetime_split / 2; i++) { - SWAP(particle_array[i], particle_array[lifetime_split - i]); + SWAP(particle_array[i], particle_array[lifetime_split - i - 1]); } for (uint32_t i = 0; i < (particles->amount - lifetime_split) / 2; i++) { - SWAP(particle_array[lifetime_split + i + 1], particle_array[particles->amount - 1 - i]); + SWAP(particle_array[lifetime_split + i], particle_array[particles->amount - 1 - i]); } #ifndef __EMSCRIPTEN__ diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h index b220c48de9..0e84c2ca8f 100644 --- a/drivers/gles3/storage/particles_storage.h +++ b/drivers/gles3/storage/particles_storage.h @@ -33,10 +33,10 @@ #ifdef GLES3_ENABLED -#include "../shaders/particles_copy.glsl.gen.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" +#include "drivers/gles3/shaders/particles_copy.glsl.gen.h" #include "servers/rendering/storage/particles_storage.h" #include "servers/rendering/storage/utilities.h" diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index b4dea0dfe2..3fd54e9180 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1140,6 +1140,13 @@ RID TextureStorage::texture_get_rd_texture(RID p_texture, bool p_srgb) const { return RID(); } +uint64_t TextureStorage::texture_get_native_handle(RID p_texture, bool p_srgb) const { + const Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!texture, 0); + + return texture->tex_id; +} + void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) { Texture *texture = texture_owner.get_or_null(p_texture); @@ -1782,7 +1789,64 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } } +void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture) { + if (rt->backbuffer != 0 && rt->backbuffer_depth != 0) { + return; + } + + Config *config = Config::get_singleton(); + bool use_multiview = rt->view_count > 1 && config->multiview_supported; + GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + if (rt->backbuffer_fbo == 0) { + glGenFramebuffers(1, &rt->backbuffer_fbo); + } + glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); + if (rt->backbuffer == 0 && uses_screen_texture) { + glGenTextures(1, &rt->backbuffer); + glBindTexture(texture_target, rt->backbuffer); + if (use_multiview) { + glTexImage3D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr); + } else { + glTexImage2D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); + } + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->backbuffer, 0, 0, rt->view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, 0); + } + } + if (rt->backbuffer_depth == 0 && uses_depth_texture) { + glGenTextures(1, &rt->backbuffer_depth); + glBindTexture(texture_target, rt->backbuffer_depth); + if (use_multiview) { + glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } else { + glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->backbuffer_depth, 0, 0, rt->view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->backbuffer_depth, 0); + } + } +} void TextureStorage::_clear_render_target(RenderTarget *rt) { // there is nothing to clear when DIRECT_TO_SCREEN is used if (rt->direct_to_screen) { @@ -1848,6 +1912,10 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { rt->backbuffer = 0; rt->backbuffer_fbo = 0; } + if (rt->backbuffer_depth != 0) { + glDeleteTextures(1, &rt->backbuffer_depth); + rt->backbuffer_depth = 0; + } _render_target_clear_sdf(rt); } diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index c702904648..c6bdd39dbd 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -39,7 +39,7 @@ #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/storage/texture_storage.h" -#include "../shaders/canvas_sdf.glsl.gen.h" +#include "drivers/gles3/shaders/canvas_sdf.glsl.gen.h" // This must come first to avoid windows.h mess #include "platform_config.h" @@ -345,6 +345,7 @@ struct RenderTarget { GLuint depth = 0; GLuint backbuffer_fbo = 0; GLuint backbuffer = 0; + GLuint backbuffer_depth = 0; GLuint color_internal_format = GL_RGBA8; GLuint color_format = GL_RGBA; @@ -536,6 +537,7 @@ public: virtual Size2 texture_size_with_proxy(RID p_proxy) override; virtual RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) const override; + virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const override; void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer = 0); @@ -604,6 +606,8 @@ public: RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }; bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); }; + void copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture); + virtual RID render_target_create() override; virtual void render_target_free(RID p_rid) override; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 797ffd67fe..ab94d8911f 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -222,8 +222,8 @@ Error AudioDriverPulseAudio::init_output_device() { break; } - int latency = GLOBAL_GET("audio/driver/output_latency"); - buffer_frames = closest_power_of_2(latency * mix_rate / 1000); + int tmp_latency = GLOBAL_GET("audio/driver/output_latency"); + buffer_frames = closest_power_of_2(tmp_latency * mix_rate / 1000); pa_buffer_size = buffer_frames * pa_map.channels; print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels"); @@ -290,10 +290,22 @@ Error AudioDriverPulseAudio::init() { return ERR_CANT_OPEN; } #endif + bool ver_ok = false; + String version = String::utf8(pa_get_library_version()); + Vector<String> ver_parts = version.split("."); + if (ver_parts.size() >= 2) { + ver_ok = (ver_parts[0].to_int() >= 8); // 8.0.0 + } + print_verbose(vformat("PulseAudio %s detected.", version)); + if (!ver_ok) { + print_verbose("Unsupported PulseAudio library version!"); + return ERR_CANT_OPEN; + } + active.clear(); exit_thread.clear(); - mix_rate = GLOBAL_GET("audio/driver/mix_rate"); + mix_rate = _get_configured_mix_rate(); pa_ml = pa_mainloop_new(); ERR_FAIL_COND_V(pa_ml == nullptr, ERR_CANT_OPEN); @@ -664,7 +676,9 @@ void AudioDriverPulseAudio::finish() { } exit_thread.set(); - thread.wait_to_finish(); + if (thread.is_started()) { + thread.wait_to_finish(); + } finish_output_device(); diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 3c2f36c7c0..e9affe41af 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -301,11 +301,11 @@ bool FileAccessUnix::file_exists(const String &p_path) { uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { String file = fix_path(p_file); - struct stat flags = {}; - int err = stat(file.utf8().get_data(), &flags); + struct stat status = {}; + int err = stat(file.utf8().get_data(), &status); if (!err) { - return flags.st_mtime; + return status.st_mtime; } else { print_verbose("Failed to get modified time for: " + p_file + ""); return 0; @@ -314,11 +314,11 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) { String file = fix_path(p_file); - struct stat flags = {}; - int err = stat(file.utf8().get_data(), &flags); + struct stat status = {}; + int err = stat(file.utf8().get_data(), &status); if (!err) { - return flags.st_mode & 0x7FF; //only permissions + return status.st_mode & 0x7FF; //only permissions } else { ERR_FAIL_V_MSG(0, "Failed to get unix permissions for: " + p_file + "."); } diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 178f01b185..967699c5f3 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -41,9 +41,12 @@ #include "drivers/unix/thread_posix.h" #include "servers/rendering_server.h" -#ifdef __APPLE__ +#if defined(__APPLE__) #include <mach-o/dyld.h> +#include <mach/host_info.h> +#include <mach/mach_host.h> #include <mach/mach_time.h> +#include <sys/sysctl.h> #endif #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) @@ -51,6 +54,19 @@ #include <sys/sysctl.h> #endif +#if defined(__FreeBSD__) +#include <kvm.h> +#endif + +#if defined(__OpenBSD__) +#include <sys/swap.h> +#include <uvm/uvmexp.h> +#endif + +#if defined(__NetBSD__) +#include <uvm/uvm_extern.h> +#endif + #include <dlfcn.h> #include <errno.h> #include <poll.h> @@ -59,6 +75,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/resource.h> #include <sys/time.h> #include <sys/wait.h> #include <time.h> @@ -274,6 +291,192 @@ uint64_t OS_Unix::get_ticks_usec() const { return longtime; } +Dictionary OS_Unix::get_memory_info() const { + Dictionary meminfo; + + meminfo["physical"] = -1; + meminfo["free"] = -1; + meminfo["available"] = -1; + meminfo["stack"] = -1; + +#if defined(__APPLE__) + int pagesize = 0; + size_t len = sizeof(pagesize); + if (sysctlbyname("vm.pagesize", &pagesize, &len, nullptr, 0) < 0) { + ERR_PRINT(vformat("Could not get vm.pagesize, error code: %d - %s", errno, strerror(errno))); + } + + int64_t phy_mem = 0; + len = sizeof(phy_mem); + if (sysctlbyname("hw.memsize", &phy_mem, &len, nullptr, 0) < 0) { + ERR_PRINT(vformat("Could not get hw.memsize, error code: %d - %s", errno, strerror(errno))); + } + + mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; + vm_statistics64_data_t vmstat; + if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vmstat, &count) != KERN_SUCCESS) { + ERR_PRINT(vformat("Could not get host vm statistics.")); + } + struct xsw_usage swap_used; + len = sizeof(swap_used); + if (sysctlbyname("vm.swapusage", &swap_used, &len, nullptr, 0) < 0) { + ERR_PRINT(vformat("Could not get vm.swapusage, error code: %d - %s", errno, strerror(errno))); + } + + if (phy_mem != 0) { + meminfo["physical"] = phy_mem; + } + if (vmstat.free_count * (int64_t)pagesize != 0) { + meminfo["free"] = vmstat.free_count * (int64_t)pagesize; + } + if (swap_used.xsu_avail + vmstat.free_count * (int64_t)pagesize != 0) { + meminfo["available"] = swap_used.xsu_avail + vmstat.free_count * (int64_t)pagesize; + } +#elif defined(__FreeBSD__) + int pagesize = 0; + size_t len = sizeof(pagesize); + if (sysctlbyname("vm.stats.vm.v_page_size", &pagesize, &len, nullptr, 0) < 0) { + ERR_PRINT(vformat("Could not get vm.stats.vm.v_page_size, error code: %d - %s", errno, strerror(errno))); + } + + uint64_t mtotal = 0; + len = sizeof(mtotal); + if (sysctlbyname("vm.stats.vm.v_page_count", &mtotal, &len, nullptr, 0) < 0) { + ERR_PRINT(vformat("Could not get vm.stats.vm.v_page_count, error code: %d - %s", errno, strerror(errno))); + } + uint64_t mfree = 0; + len = sizeof(mfree); + if (sysctlbyname("vm.stats.vm.v_free_count", &mfree, &len, nullptr, 0) < 0) { + ERR_PRINT(vformat("Could not get vm.stats.vm.v_free_count, error code: %d - %s", errno, strerror(errno))); + } + + uint64_t stotal = 0; + uint64_t sused = 0; + char errmsg[_POSIX2_LINE_MAX] = {}; + kvm_t *kd = kvm_openfiles(nullptr, "/dev/null", nullptr, 0, errmsg); + if (kd == nullptr) { + ERR_PRINT(vformat("kvm_openfiles failed, error: %s", errmsg)); + } else { + struct kvm_swap swap_info[32]; + int count = kvm_getswapinfo(kd, swap_info, 32, 0); + for (int i = 0; i < count; i++) { + stotal += swap_info[i].ksw_total; + sused += swap_info[i].ksw_used; + } + kvm_close(kd); + } + + if (mtotal * pagesize != 0) { + meminfo["physical"] = mtotal * pagesize; + } + if (mfree * pagesize != 0) { + meminfo["free"] = mfree * pagesize; + } + if ((mfree + stotal - sused) * pagesize != 0) { + meminfo["available"] = (mfree + stotal - sused) * pagesize; + } +#elif defined(__OpenBSD__) + int pagesize = sysconf(_SC_PAGESIZE); + + const int mib[] = { CTL_VM, VM_UVMEXP }; + uvmexp uvmexp_info; + size_t len = sizeof(uvmexp_info); + if (sysctl(mib, 2, &uvmexp_info, &len, nullptr, 0) < 0) { + ERR_PRINT(vformat("Could not get CTL_VM, VM_UVMEXP, error code: %d - %s", errno, strerror(errno))); + } + + uint64_t stotal = 0; + uint64_t sused = 0; + int count = swapctl(SWAP_NSWAP, 0, 0); + if (count > 0) { + swapent swap_info[count]; + count = swapctl(SWAP_STATS, swap_info, count); + + for (int i = 0; i < count; i++) { + if (swap_info[i].se_flags & SWF_ENABLE) { + sused += swap_info[i].se_inuse; + stotal += swap_info[i].se_nblks; + } + } + } + + if (uvmexp_info.npages * pagesize != 0) { + meminfo["physical"] = uvmexp_info.npages * pagesize; + } + if (uvmexp_info.free * pagesize != 0) { + meminfo["free"] = uvmexp_info.free * pagesize; + } + if ((uvmexp_info.free * pagesize) + (stotal - sused) * DEV_BSIZE != 0) { + meminfo["available"] = (uvmexp_info.free * pagesize) + (stotal - sused) * DEV_BSIZE; + } +#elif defined(__NetBSD__) + int pagesize = sysconf(_SC_PAGESIZE); + + const int mib[] = { CTL_VM, VM_UVMEXP2 }; + uvmexp_sysctl uvmexp_info; + size_t len = sizeof(uvmexp_info); + if (sysctl(mib, 2, &uvmexp_info, &len, nullptr, 0) < 0) { + ERR_PRINT(vformat("Could not get CTL_VM, VM_UVMEXP2, error code: %d - %s", errno, strerror(errno))); + } + + if (uvmexp_info.npages * pagesize != 0) { + meminfo["physical"] = uvmexp_info.npages * pagesize; + } + if (uvmexp_info.free * pagesize != 0) { + meminfo["free"] = uvmexp_info.free * pagesize; + } + if ((uvmexp_info.free + uvmexp_info.swpages - uvmexp_info.swpginuse) * pagesize != 0) { + meminfo["available"] = (uvmexp_info.free + uvmexp_info.swpages - uvmexp_info.swpginuse) * pagesize; + } +#else + Error err; + Ref<FileAccess> f = FileAccess::open("/proc/meminfo", FileAccess::READ, &err); + uint64_t mtotal = 0; + uint64_t mfree = 0; + uint64_t sfree = 0; + while (f.is_valid() && !f->eof_reached()) { + String s = f->get_line().strip_edges(); + if (s.begins_with("MemTotal:")) { + Vector<String> stok = s.replace("MemTotal:", "").strip_edges().split(" "); + if (stok.size() == 2) { + mtotal = stok[0].to_int() * 1024; + } + } + if (s.begins_with("MemFree:")) { + Vector<String> stok = s.replace("MemFree:", "").strip_edges().split(" "); + if (stok.size() == 2) { + mfree = stok[0].to_int() * 1024; + } + } + if (s.begins_with("SwapFree:")) { + Vector<String> stok = s.replace("SwapFree:", "").strip_edges().split(" "); + if (stok.size() == 2) { + sfree = stok[0].to_int() * 1024; + } + } + } + + if (mtotal != 0) { + meminfo["physical"] = mtotal; + } + if (mfree != 0) { + meminfo["free"] = mfree; + } + if (mfree + sfree != 0) { + meminfo["available"] = mfree + sfree; + } +#endif + + rlimit stackinfo = {}; + getrlimit(RLIMIT_STACK, &stackinfo); + + if (stackinfo.rlim_cur != 0) { + meminfo["stack"] = (int64_t)stackinfo.rlim_cur; + } + + return meminfo; +} + 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. diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index 03429622ae..86b0e38e92 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -73,6 +73,8 @@ public: virtual void delay_usec(uint32_t p_usec) const override; virtual uint64_t get_ticks_usec() const override; + 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 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; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 2166d12d31..69d9baf910 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -1825,7 +1825,6 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T } if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT && !(flags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - printf("vkformat: %x\n", image_create_info.format); ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as depth-stencil attachment."); } @@ -2463,10 +2462,10 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co } ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE, - "Texture can't be updated while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + "Texture can't be updated while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to update this texture."); ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER, - "Texture requires the TEXTURE_USAGE_CAN_UPDATE_BIT in order to be updatable."); + "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT` to be set to be updatable."); uint32_t layer_count = texture->layers; if (texture->type == TEXTURE_TYPE_CUBE || texture->type == TEXTURE_TYPE_CUBE_ARRAY) { @@ -2739,9 +2738,9 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t ERR_FAIL_COND_V(!tex, Vector<uint8_t>()); ERR_FAIL_COND_V_MSG(tex->bound, Vector<uint8_t>(), - "Texture can't be retrieved while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + "Texture can't be retrieved while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to retrieve this texture."); ERR_FAIL_COND_V_MSG(!(tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), Vector<uint8_t>(), - "Texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order to be retrieved."); + "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); uint32_t layer_count = tex->layers; if (tex->type == TEXTURE_TYPE_CUBE || tex->type == TEXTURE_TYPE_CUBE_ARRAY) { @@ -2882,6 +2881,15 @@ Size2i RenderingDeviceVulkan::texture_size(RID p_texture) { return Size2i(tex->width, tex->height); } +uint64_t RenderingDeviceVulkan::texture_native_handle(RID p_texture) { + _THREAD_SAFE_METHOD_ + + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!tex, 0); + + return (uint64_t)tex->image; +} + Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier) { _THREAD_SAFE_METHOD_ @@ -2889,9 +2897,9 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, - "Source texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + "Source texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, - "Source texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order to be retrieved."); + "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); uint32_t src_layer_count = src_tex->layers; uint32_t src_width, src_height, src_depth; @@ -2910,9 +2918,9 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, ERR_FAIL_COND_V(!dst_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER, - "Destination texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + "Destination texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, - "Destination texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order to be retrieved."); + "Destination texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be retrieved."); uint32_t dst_layer_count = dst_tex->layers; uint32_t dst_width, dst_height, dst_depth; @@ -3084,9 +3092,9 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, - "Source texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + "Source texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, - "Source texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order to be retrieved."); + "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); ERR_FAIL_COND_V_MSG(src_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Source texture must be 2D (or a slice of a 3D/Cube texture)"); ERR_FAIL_COND_V_MSG(src_tex->samples == TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Source texture must be multisampled."); @@ -3095,9 +3103,9 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID ERR_FAIL_COND_V(!dst_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER, - "Destination texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + "Destination texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture."); ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, - "Destination texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order to be retrieved."); + "Destination texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be retrieved."); ERR_FAIL_COND_V_MSG(dst_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Destination texture must be 2D (or a slice of a 3D/Cube texture)."); ERR_FAIL_COND_V_MSG(dst_tex->samples != TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Destination texture must not be multisampled."); @@ -3255,13 +3263,13 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER, - "Source texture can't be cleared while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture."); + "Source texture can't be cleared while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to clear this texture."); ERR_FAIL_COND_V(p_layers == 0, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_mipmaps == 0, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, - "Source texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order to be cleared."); + "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be cleared."); uint32_t src_layer_count = src_tex->layers; if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { @@ -4306,6 +4314,18 @@ RID RenderingDeviceVulkan::sampler_create(const SamplerState &p_state) { return id; } +bool RenderingDeviceVulkan::sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const { + ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false); + + _THREAD_SAFE_METHOD_ + + // Validate that this image is supported for the intended filtering. + VkFormatProperties properties; + vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), vulkan_formats[p_format], &properties); + + return p_sampler_filter == RD::SAMPLER_FILTER_NEAREST || (p_sampler_filter == RD::SAMPLER_FILTER_LINEAR && (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)); +} + /**********************/ /**** VERTEX ARRAY ****/ /**********************/ @@ -4314,10 +4334,6 @@ RID RenderingDeviceVulkan::vertex_buffer_create(uint32_t p_size_bytes, const Vec _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); - ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); - ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); uint32_t usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; if (p_use_as_storage) { @@ -4455,10 +4471,6 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBufferFormat p_format, const Vector<uint8_t> &p_data, bool p_use_restart_indices) { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); - ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); ERR_FAIL_COND_V(p_index_count == 0, RID()); @@ -5141,10 +5153,6 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); - ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); - ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); Buffer buffer; Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); @@ -5164,10 +5172,6 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, BitField<StorageBufferUsage> p_usage) { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); - ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); @@ -5190,10 +5194,6 @@ RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Ve RID RenderingDeviceVulkan::texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data) { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); - ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), - "Creating buffers with data is forbidden during creation of a draw list"); uint32_t element_size = get_format_vertex_size(p_format); ERR_FAIL_COND_V_MSG(element_size == 0, RID(), "Format requested is not supported for texture buffers"); @@ -8115,35 +8115,14 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, } void RenderingDeviceVulkan::compute_list_add_barrier(ComputeListID p_list) { -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else - _memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, true); -#endif + uint32_t barrier_flags = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + uint32_t access_flags = VK_ACCESS_SHADER_READ_BIT; + _compute_list_add_barrier(BARRIER_MASK_COMPUTE, barrier_flags, access_flags); } -void RenderingDeviceVulkan::compute_list_end(BitField<BarrierMask> p_post_barrier) { +void RenderingDeviceVulkan::_compute_list_add_barrier(BitField<BarrierMask> p_post_barrier, uint32_t p_barrier_flags, uint32_t p_access_flags) { ERR_FAIL_COND(!compute_list); - uint32_t barrier_flags = 0; - uint32_t access_flags = 0; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { - barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; - access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; - } - - if (barrier_flags == 0) { - barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - VkImageMemoryBarrier *image_barriers = nullptr; uint32_t image_barrier_count = compute_list->state.textures_to_sampled_layout.size(); @@ -8152,27 +8131,29 @@ void RenderingDeviceVulkan::compute_list_end(BitField<BarrierMask> p_post_barrie image_barriers = (VkImageMemoryBarrier *)alloca(sizeof(VkImageMemoryBarrier) * image_barrier_count); } - uint32_t barrier_idx = 0; + image_barrier_count = 0; // We'll count how many we end up issuing. for (Texture *E : compute_list->state.textures_to_sampled_layout) { - VkImageMemoryBarrier &image_memory_barrier = image_barriers[barrier_idx++]; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = E->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + if (E->layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + VkImageMemoryBarrier &image_memory_barrier = image_barriers[image_barrier_count++]; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = nullptr; + image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.dstAccessMask = p_access_flags; + image_memory_barrier.oldLayout = E->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = E->image; - image_memory_barrier.subresourceRange.aspectMask = E->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = E->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = E->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = E->base_layer; - image_memory_barrier.subresourceRange.layerCount = E->layers; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = E->image; + image_memory_barrier.subresourceRange.aspectMask = E->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = E->base_mipmap; + image_memory_barrier.subresourceRange.levelCount = E->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = E->base_layer; + image_memory_barrier.subresourceRange.layerCount = E->layers; - E->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + E->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } if (E->used_in_frame != frames_drawn) { E->used_in_transfer = false; @@ -8182,19 +8163,40 @@ void RenderingDeviceVulkan::compute_list_end(BitField<BarrierMask> p_post_barrie } } - VkMemoryBarrier mem_barrier; - mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; - mem_barrier.pNext = nullptr; - mem_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - mem_barrier.dstAccessMask = access_flags; - - if (image_barrier_count > 0 || p_post_barrier != BARRIER_MASK_NO_BARRIER) { - vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, barrier_flags, 0, 1, &mem_barrier, 0, nullptr, image_barrier_count, image_barriers); + if (p_barrier_flags) { + VkMemoryBarrier mem_barrier; + mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + mem_barrier.pNext = nullptr; + mem_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + mem_barrier.dstAccessMask = p_access_flags; + vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, p_barrier_flags, 0, 1, &mem_barrier, 0, nullptr, image_barrier_count, image_barriers); + } else if (image_barrier_count) { + vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, image_barrier_count, image_barriers); } #ifdef FORCE_FULL_BARRIER _full_barrier(true); #endif +} + +void RenderingDeviceVulkan::compute_list_end(BitField<BarrierMask> p_post_barrier) { + ERR_FAIL_COND(!compute_list); + + uint32_t barrier_flags = 0; + uint32_t access_flags = 0; + if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { + barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) { + barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + } + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; + access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; + } + _compute_list_add_barrier(p_post_barrier, barrier_flags, access_flags); memdelete(compute_list); compute_list = nullptr; @@ -8922,6 +8924,11 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de } } + for (int i = 0; i < frame_count; i++) { + //Reset all queries in a query pool before doing any operations with them. + vkCmdResetQueryPool(frames[0].setup_command_buffer, frames[i].timestamp_pool, 0, max_timestamp_query_elements); + } + staging_buffer_block_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/block_size_kb"); staging_buffer_block_size = MAX(4u, staging_buffer_block_size); staging_buffer_block_size *= 1024; // Kb -> bytes. @@ -9387,6 +9394,9 @@ bool RenderingDeviceVulkan::has_feature(const Features p_feature) const { VulkanContext::VRSCapabilities vrs_capabilities = context->get_vrs_capabilities(); return vrs_capabilities.attachment_vrs_supported && context->get_physical_device_features().shaderStorageImageExtendedFormats; } break; + case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: { + return true; + } break; default: { return false; } diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 91a09fa970..4150e0a8e6 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -954,6 +954,8 @@ class RenderingDeviceVulkan : public RenderingDevice { ComputeList *compute_list = nullptr; + void _compute_list_add_barrier(BitField<BarrierMask> p_post_barrier, uint32_t p_barrier_flags, uint32_t p_access_flags); + /**************************/ /**** FRAME MANAGEMENT ****/ /**************************/ @@ -1055,6 +1057,7 @@ public: virtual bool texture_is_shared(RID p_texture); virtual bool texture_is_valid(RID p_texture); virtual Size2i texture_size(RID p_texture); + virtual uint64_t texture_native_handle(RID p_texture); virtual Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); virtual Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); @@ -1082,6 +1085,7 @@ public: /*****************/ virtual RID sampler_create(const SamplerState &p_state); + virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const; /**********************/ /**** VERTEX ARRAY ****/ diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index f185a5cb88..f65056951d 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -2274,8 +2274,8 @@ Error VulkanContext::prepare_buffers() { } else if (err == VK_SUBOPTIMAL_KHR) { // Swapchain is not as optimal as it could be, but the platform's // presentation engine will still present the image correctly. - print_verbose("Vulkan: Early suboptimal swapchain."); - break; + print_verbose("Vulkan: Early suboptimal swapchain, recreating."); + _update_swap_chain(w); } else if (err != VK_SUCCESS) { ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully. Error code: " + String(string_VkResult(err))); } else { diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 72ec0c19ab..eeeb6ee689 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -206,8 +206,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i IMMDeviceEnumerator *enumerator = nullptr; IMMDevice *output_device = nullptr; - CoInitialize(nullptr); - HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); @@ -482,6 +480,14 @@ Error AudioDriverWASAPI::init_output_device(bool p_reinit) { } switch (audio_output.channels) { + case 1: // Mono + case 3: // Surround 2.1 + case 5: // Surround 5.0 + case 7: // Surround 7.0 + // We will downmix as required. + channels = audio_output.channels + 1; + break; + case 2: // Stereo case 4: // Surround 3.1 case 6: // Surround 5.1 @@ -501,7 +507,7 @@ Error AudioDriverWASAPI::init_output_device(bool p_reinit) { input_position = 0; input_size = 0; - print_verbose("WASAPI: detected " + itos(channels) + " channels"); + print_verbose("WASAPI: detected " + itos(audio_output.channels) + " channels"); print_verbose("WASAPI: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); return OK; @@ -547,7 +553,7 @@ Error AudioDriverWASAPI::finish_input_device() { } Error AudioDriverWASAPI::init() { - mix_rate = GLOBAL_GET("audio/driver/mix_rate"); + mix_rate = _get_configured_mix_rate(); target_latency_ms = GLOBAL_GET("audio/driver/output_latency"); @@ -582,8 +588,6 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) { list.push_back(String("Default")); - CoInitialize(nullptr); - HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); @@ -702,6 +706,8 @@ void AudioDriverWASAPI::write_sample(WORD format_tag, int bits_per_sample, BYTE } void AudioDriverWASAPI::thread_func(void *p_udata) { + CoInitializeEx(nullptr, COINIT_MULTITHREADED); + AudioDriverWASAPI *ad = static_cast<AudioDriverWASAPI *>(p_udata); uint32_t avail_frames = 0; uint32_t write_ofs = 0; @@ -748,6 +754,19 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { for (unsigned int i = 0; i < write_frames * ad->channels; i++) { ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i, ad->samples_in.write[write_ofs++]); } + } else if (ad->channels == ad->audio_output.channels + 1) { + // Pass all channels except the last two as-is, and then mix the last two + // together as one channel. E.g. stereo -> mono, or 3.1 -> 2.1. + unsigned int last_chan = ad->audio_output.channels - 1; + for (unsigned int i = 0; i < write_frames; i++) { + for (unsigned int j = 0; j < last_chan; j++) { + ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i * ad->audio_output.channels + j, ad->samples_in.write[write_ofs++]); + } + int32_t l = ad->samples_in.write[write_ofs++]; + int32_t r = ad->samples_in.write[write_ofs++]; + int32_t c = (int32_t)(((int64_t)l + (int64_t)r) / 2); + ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i * ad->audio_output.channels + last_chan, c); + } } else { for (unsigned int i = 0; i < write_frames; i++) { for (unsigned int j = 0; j < MIN(ad->channels, ad->audio_output.channels); j++) { @@ -908,6 +927,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { OS::get_singleton()->delay_usec(1000); } } + CoUninitialize(); } void AudioDriverWASAPI::start() { @@ -931,7 +951,9 @@ void AudioDriverWASAPI::unlock() { void AudioDriverWASAPI::finish() { exit_thread.set(); - thread.wait_to_finish(); + if (thread.is_started()) { + thread.wait_to_finish(); + } finish_input_device(); finish_output_device(); diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 7b88bd8a95..88eb89656a 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -59,6 +59,14 @@ struct DirAccessWindowsPrivate { WIN32_FIND_DATAW fu; //unicode version }; +String DirAccessWindows::fix_path(String p_path) const { + String r_path = DirAccess::fix_path(p_path); + if (r_path.is_absolute_path() && !r_path.is_network_share_path() && r_path.length() > MAX_PATH) { + r_path = "\\\\?\\" + r_path.replace("/", "\\"); + } + return r_path; +} + // CreateFolderAsync Error DirAccessWindows::list_dir_begin() { @@ -158,6 +166,7 @@ Error DirAccessWindows::make_dir(String p_dir) { p_dir = fix_path(p_dir); if (p_dir.is_relative_path()) { p_dir = current_dir.path_join(p_dir); + p_dir = fix_path(p_dir); } p_dir = p_dir.simplify_path().replace("/", "\\"); @@ -165,12 +174,6 @@ Error DirAccessWindows::make_dir(String p_dir) { bool success; int err; - if (!p_dir.is_network_share_path()) { - p_dir = "\\\\?\\" + p_dir; - // Add "\\?\" to the path to extend max. path length past 248, if it's not a network share UNC path. - // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx - } - success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), nullptr); err = GetLastError(); diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h index 9d91c22f7e..1e55917756 100644 --- a/drivers/windows/dir_access_windows.h +++ b/drivers/windows/dir_access_windows.h @@ -53,6 +53,9 @@ class DirAccessWindows : public DirAccess { bool _cisdir = false; bool _cishidden = false; +protected: + virtual String fix_path(String p_path) const override; + public: virtual Error list_dir_begin() override; ///< This starts dir listing virtual String get_next() override; diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index f36bbcc350..6e69743d4e 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -68,6 +68,14 @@ bool FileAccessWindows::is_path_invalid(const String &p_path) { return invalid_files.has(fname); } +String FileAccessWindows::fix_path(const String &p_path) const { + String r_path = FileAccess::fix_path(p_path); + if (r_path.is_absolute_path() && !r_path.is_network_share_path() && r_path.length() > MAX_PATH) { + r_path = "\\\\?\\" + r_path.replace("/", "\\"); + } + return r_path; +} + Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { if (is_path_invalid(p_path)) { #ifdef DEBUG_ENABLED diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 453f8d3b5f..13c881e562 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -54,6 +54,7 @@ class FileAccessWindows : public FileAccess { static HashSet<String> invalid_files; public: + virtual String fix_path(const String &p_path) const override; 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 diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index 44ce01d4d7..22063c52d9 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -39,7 +39,8 @@ Error AudioDriverXAudio2::init() { pcm_open = false; samples_in = nullptr; - mix_rate = GLOBAL_GET("audio/driver/mix_rate"); + mix_rate = _get_configured_mix_rate(); + // FIXME: speaker_mode seems unused in the Xaudio2 driver so far speaker_mode = SPEAKER_MODE_STEREO; channels = 2; @@ -150,7 +151,9 @@ void AudioDriverXAudio2::unlock() { void AudioDriverXAudio2::finish() { exit_thread.set(); - thread.wait_to_finish(); + if (thread.is_started()) { + thread.wait_to_finish(); + } if (source_voice) { source_voice->Stop(0); |