diff options
64 files changed, 1384 insertions, 304 deletions
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index fc3547261b..22bf8571bf 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/io/file_access.h" #include "core/io/resource_importer.h" +#include "core/os/condition_variable.h" #include "core/os/os.h" #include "core/string/print_string.h" #include "core/string/translation.h" @@ -233,7 +234,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { ThreadLoadTask &load_task = *(ThreadLoadTask *)p_userdata; load_task.loader_id = Thread::get_caller_id(); - if (load_task.semaphore) { + if (load_task.cond_var) { //this is an actual thread, so wait for Ok from semaphore thread_load_semaphore->wait(); //wait until its ok to start loading } @@ -247,7 +248,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { } else { load_task.status = THREAD_LOAD_LOADED; } - if (load_task.semaphore) { + if (load_task.cond_var) { if (load_task.start_next && thread_waiting_count > 0) { thread_waiting_count--; //thread loading count remains constant, this ends but another one begins @@ -258,11 +259,9 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { print_lt("END: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count)); - for (int i = 0; i < load_task.poll_requests; i++) { - load_task.semaphore->post(); - } - memdelete(load_task.semaphore); - load_task.semaphore = nullptr; + load_task.cond_var->notify_all(); + memdelete(load_task.cond_var); + load_task.cond_var = nullptr; } if (load_task.resource.is_valid()) { @@ -373,7 +372,7 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String & if (load_task.resource.is_null()) { //needs to be loaded in thread - load_task.semaphore = memnew(Semaphore); + load_task.cond_var = memnew(ConditionVariable); if (thread_loading_count < thread_load_max) { thread_loading_count++; thread_load_semaphore->post(); //we have free threads, so allow one @@ -438,9 +437,8 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const Ref<Resource> ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) { String local_path = _validate_local_path(p_path); - thread_load_mutex->lock(); + MutexLock thread_load_lock(*thread_load_mutex); if (!thread_load_tasks.has(local_path)) { - thread_load_mutex->unlock(); if (r_error) { *r_error = ERR_INVALID_PARAMETER; } @@ -449,13 +447,21 @@ Ref<Resource> ResourceLoader::load_threaded_get(const String &p_path, Error *r_e ThreadLoadTask &load_task = thread_load_tasks[local_path]; - //semaphore still exists, meaning it's still loading, request poll - Semaphore *semaphore = load_task.semaphore; - if (semaphore) { - load_task.poll_requests++; + if (!load_task.cond_var && load_task.status == THREAD_LOAD_IN_PROGRESS) { + // A condition variable was never created for this task. + // That happens when a load has been initiated with subthreads disabled, + // but now another load thread needs to interact with this one (either + // because of subthreads being used this time, or because it's simply a + // threaded load running on a different thread). + // Since we want to be notified when the load ends, we must create the + // condition variable now. + load_task.cond_var = memnew(ConditionVariable); + } + //cond var still exists, meaning it's still loading, request poll + if (load_task.cond_var) { { - // As we got a semaphore, this means we are going to have to wait + // As we got a cond var, this means we are going to have to wait // until the sub-resource is done loading // // As this thread will become 'blocked' we should "exchange" its @@ -477,14 +483,13 @@ Ref<Resource> ResourceLoader::load_threaded_get(const String &p_path, Error *r_e print_lt("GET: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count)); } - thread_load_mutex->unlock(); - semaphore->wait(); - thread_load_mutex->lock(); + do { + load_task.cond_var->wait(thread_load_lock); + } while (load_task.cond_var); // In case of spurious wakeup. thread_suspended_count--; if (!thread_load_tasks.has(local_path)) { //may have been erased during unlock and this was always an invalid call - thread_load_mutex->unlock(); if (r_error) { *r_error = ERR_INVALID_PARAMETER; } @@ -507,8 +512,6 @@ Ref<Resource> ResourceLoader::load_threaded_get(const String &p_path, Error *r_e thread_load_tasks.erase(local_path); } - thread_load_mutex->unlock(); - return resource; } @@ -1067,7 +1070,7 @@ void ResourceLoader::remove_custom_loaders() { } void ResourceLoader::initialize() { - thread_load_mutex = memnew(Mutex); + thread_load_mutex = memnew(SafeBinaryMutex<BINARY_MUTEX_TAG>); thread_load_max = OS::get_singleton()->get_processor_count(); thread_loading_count = 0; thread_waiting_count = 0; @@ -1090,7 +1093,9 @@ bool ResourceLoader::create_missing_resources_if_class_unavailable = false; bool ResourceLoader::abort_on_missing_resource = true; bool ResourceLoader::timestamp_on_load = false; -Mutex *ResourceLoader::thread_load_mutex = nullptr; +template <> +thread_local uint32_t SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG>::count = 0; +SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG> *ResourceLoader::thread_load_mutex = nullptr; HashMap<String, ResourceLoader::ThreadLoadTask> ResourceLoader::thread_load_tasks; Semaphore *ResourceLoader::thread_load_semaphore = nullptr; diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index c47b6c950a..72c1f90653 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -37,6 +37,8 @@ #include "core/os/semaphore.h" #include "core/os/thread.h" +class ConditionVariable; + class ResourceFormatLoader : public RefCounted { GDCLASS(ResourceFormatLoader, RefCounted); @@ -105,6 +107,8 @@ public: THREAD_LOAD_LOADED }; + static const int BINARY_MUTEX_TAG = 1; + private: static Ref<ResourceFormatLoader> loader[MAX_LOADERS]; static int loader_count; @@ -136,7 +140,7 @@ private: struct ThreadLoadTask { Thread *thread = nullptr; Thread::ID loader_id = 0; - Semaphore *semaphore = nullptr; + ConditionVariable *cond_var = nullptr; String local_path; String remapped_path; String type_hint; @@ -149,12 +153,11 @@ private: bool use_sub_threads = false; bool start_next = true; int requests = 0; - int poll_requests = 0; HashSet<String> sub_tasks; }; static void _thread_load_function(void *p_userdata); - static Mutex *thread_load_mutex; + static SafeBinaryMutex<BINARY_MUTEX_TAG> *thread_load_mutex; static HashMap<String, ThreadLoadTask> thread_load_tasks; static Semaphore *thread_load_semaphore; static int thread_waiting_count; diff --git a/core/os/condition_variable.h b/core/os/condition_variable.h new file mode 100644 index 0000000000..6037ff327d --- /dev/null +++ b/core/os/condition_variable.h @@ -0,0 +1,60 @@ +/**************************************************************************/ +/* condition_variable.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 CONDITION_VARIABLE_H +#define CONDITION_VARIABLE_H + +#include <condition_variable> + +// An object one or multiple threads can wait on a be notified by some other. +// Normally, you want to use a semaphore for such scenarios, but when the +// condition is something different than a count being greater than zero +// (which is the built-in logic in a semaphore) or you want to provide your +// own mutex to tie the wait-notify to some other behavior, you need to use this. + +class ConditionVariable { + mutable std::condition_variable condition; + +public: + template <class BinaryMutexT> + _ALWAYS_INLINE_ void wait(const MutexLock<BinaryMutexT> &p_lock) const { + condition.wait(const_cast<std::unique_lock<std::mutex> &>(p_lock.lock)); + } + + _ALWAYS_INLINE_ void notify_one() const { + condition.notify_one(); + } + + _ALWAYS_INLINE_ void notify_all() const { + condition.notify_all(); + } +}; + +#endif // CONDITION_VARIABLE_H diff --git a/core/os/mutex.h b/core/os/mutex.h index ceedcb235a..90cc1632e8 100644 --- a/core/os/mutex.h +++ b/core/os/mutex.h @@ -31,12 +31,20 @@ #ifndef MUTEX_H #define MUTEX_H +#include "core/error/error_macros.h" #include "core/typedefs.h" #include <mutex> +template <class MutexT> +class MutexLock; + template <class StdMutexT> class MutexImpl { + friend class MutexLock<MutexImpl<StdMutexT>>; + + using StdMutexType = StdMutexT; + mutable StdMutexT mutex; public: @@ -53,18 +61,65 @@ public: } }; +// A very special kind of mutex, used in scenarios where these +// requirements hold at the same time: +// - Must be used with a condition variable (only binary mutexes are suitable). +// - Must have recursive semnantics (or simulate, as this one does). +// The implementation keeps the lock count in TS. Therefore, only +// one object of each version of the template can exists; hence the Tag argument. +// Tags must be unique across the Godot codebase. +// Also, don't forget to declare the thread_local variable on each use. +template <int Tag> +class SafeBinaryMutex { + friend class MutexLock<SafeBinaryMutex>; + + using StdMutexType = std::mutex; + + mutable std::mutex mutex; + static thread_local uint32_t count; + +public: + _ALWAYS_INLINE_ void lock() const { + if (++count == 1) { + mutex.lock(); + } + } + + _ALWAYS_INLINE_ void unlock() const { + DEV_ASSERT(count); + if (--count == 0) { + mutex.unlock(); + } + } + + _ALWAYS_INLINE_ bool try_lock() const { + if (count) { + count++; + return true; + } else { + if (mutex.try_lock()) { + count++; + return true; + } else { + return false; + } + } + } + + ~SafeBinaryMutex() { + DEV_ASSERT(!count); + } +}; + template <class MutexT> class MutexLock { - const MutexT &mutex; + friend class ConditionVariable; + + std::unique_lock<typename MutexT::StdMutexType> lock; public: _ALWAYS_INLINE_ explicit MutexLock(const MutexT &p_mutex) : - mutex(p_mutex) { - mutex.lock(); - } - - _ALWAYS_INLINE_ ~MutexLock() { - mutex.unlock(); + lock(p_mutex.mutex) { } }; diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index faa658971d..6dbc475bdf 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -181,7 +181,7 @@ <return type="Color" /> <param index="0" name="rgbe" type="int" /> <description> - Encodes a [Color] from a RGBE9995 format integer. See [constant Image.FORMAT_RGBE9995]. + Decodes a [Color] from a RGBE9995 format integer. See [constant Image.FORMAT_RGBE9995]. </description> </method> <method name="from_string" qualifiers="static"> diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index 29a8f470a1..7f8808acc7 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -25,6 +25,13 @@ Removes all filters except for "All Files (*)". </description> </method> + <method name="get_line_edit"> + <return type="LineEdit" /> + <description> + Returns the LineEdit for the selected file. + [b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member CanvasItem.visible] property. + </description> + </method> <method name="get_vbox"> <return type="VBoxContainer" /> <description> @@ -62,6 +69,9 @@ <member name="file_mode" type="int" setter="set_file_mode" getter="get_file_mode" enum="EditorFileDialog.FileMode" default="4"> The dialog's open or save mode, which affects the selection behavior. See [enum FileMode] </member> + <member name="filters" type="PackedStringArray" setter="set_filters" getter="get_filters" default="PackedStringArray()"> + The available file type filters. For example, this shows only [code].png[/code] and [code].gd[/code] files: [code]set_filters(PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]))[/code]. Multiple file types can also be specified in a single filter. [code]"*.png, *.jpg, *.jpeg ; Supported Images"[/code] will show both PNG and JPEG files when selected. + </member> <member name="show_hidden_files" type="bool" setter="set_show_hidden_files" getter="is_showing_hidden_files" default="false"> If [code]true[/code], hidden files and directories will be visible in the [EditorFileDialog]. This property is synchronized with [member EditorSettings.filesystem/file_dialog/show_hidden_files]. </member> diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index 723e6bbf21..235e412757 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -9,7 +9,7 @@ [b]Performance:[/b] [LightmapGI] provides the best possible run-time performance for global illumination. It is suitable for low-end hardware including integrated graphics and mobile devices. [b]Note:[/b] Due to how lightmaps work, most properties only have a visible effect once lightmaps are baked again. [b]Note:[/b] Lightmap baking on [CSGShape3D]s and [PrimitiveMesh]es is not supported, as these cannot store UV2 data required for baking. - [b]Note:[/b] If no custom lightmappers are installed, [LightmapGI] can only be baked when using the Vulkan backend (Clustered or Mobile), not OpenGL. + [b]Note:[/b] If no custom lightmappers are installed, [LightmapGI] can only be baked when using the Vulkan backend (Forward+ or Mobile), not OpenGL. </description> <tutorials> </tutorials> @@ -73,7 +73,7 @@ High bake quality (slow bake times). The quality of this preset can be adjusted by changing [member ProjectSettings.rendering/lightmapping/bake_quality/high_quality_ray_count] and [member ProjectSettings.rendering/lightmapping/bake_quality/high_quality_probe_ray_count]. </constant> <constant name="BAKE_QUALITY_ULTRA" value="3" enum="BakeQuality"> - Highest bake quality (slowest bake times). The quality of this preset can be adjusted by changing [member ProjectSettings.rendering/lightmapping/bake_quality/high_quality_ray_count] and [member ProjectSettings.rendering/lightmapping/bake_quality/ultra_quality_probe_ray_count]. + Highest bake quality (slowest bake times). The quality of this preset can be adjusted by changing [member ProjectSettings.rendering/lightmapping/bake_quality/ultra_quality_ray_count] and [member ProjectSettings.rendering/lightmapping/bake_quality/ultra_quality_probe_ray_count]. </constant> <constant name="GENERATE_PROBES_DISABLED" value="0" enum="GenerateProbes"> Don't generate lightmap probes for lighting dynamic objects. diff --git a/doc/classes/LightmapperRD.xml b/doc/classes/LightmapperRD.xml index e4b68a7ef8..a3c815f58e 100644 --- a/doc/classes/LightmapperRD.xml +++ b/doc/classes/LightmapperRD.xml @@ -5,7 +5,7 @@ </brief_description> <description> LightmapperRD ("RD" stands for [RenderingDevice]) is the built-in GPU-based lightmapper for use with [LightmapGI]. On most dedicated GPUs, it can bake lightmaps much faster than most CPU-based lightmappers. LightmapperRD uses compute shaders to bake lightmaps, so it does not require CUDA or OpenCL libraries to be installed to be usable. - [b]Note:[/b] Only usable when using the Vulkan backend (Clustered or Mobile), not OpenGL. + [b]Note:[/b] Only usable when using the Vulkan backend (Forward+ or Mobile), not OpenGL. </description> <tutorials> </tutorials> diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index ece3199aab..3063e76c65 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -258,7 +258,7 @@ Indicates this custom channel contains half precision float colors, encoded as [PackedByteArray]. </constant> <constant name="ARRAY_CUSTOM_R_FLOAT" value="4" enum="ArrayCustomFormat"> - Indicates this custom channel contains full float colors, in a [PackedFloat32Array]. Only the red green channel is used. + Indicates this custom channel contains full float colors, in a [PackedFloat32Array]. Only the red channel is used. </constant> <constant name="ARRAY_CUSTOM_RG_FLOAT" value="5" enum="ArrayCustomFormat"> Indicates this custom channel contains full float colors, in a [PackedFloat32Array]. Only red and green channels are used. diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 103de8eddb..60d473a103 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -155,7 +155,7 @@ <return type="float" /> <param index="0" name="link" type="RID" /> <description> - Returns the [code]enter_cost[/code] of this [code]link[/code]. + Returns the enter cost of this [param link]. </description> </method> <method name="link_get_map" qualifiers="const"> @@ -190,7 +190,7 @@ <return type="float" /> <param index="0" name="link" type="RID" /> <description> - Returns the [code]travel_cost[/code] of this [code]link[/code]. + Returns the travel cost of this [param link]. </description> </method> <method name="link_is_bidirectional" qualifiers="const"> @@ -429,7 +429,7 @@ <return type="float" /> <param index="0" name="region" type="RID" /> <description> - Returns the [code]enter_cost[/code] of this [param region]. + Returns the enter cost of this [param region]. </description> </method> <method name="region_get_map" qualifiers="const"> @@ -457,7 +457,7 @@ <return type="float" /> <param index="0" name="region" type="RID" /> <description> - Returns the [code]travel_cost[/code] of this [param region]. + Returns the travel cost of this [param region]. </description> </method> <method name="region_owns_point" qualifiers="const"> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 0d8cb78ff9..71906729d1 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -162,7 +162,7 @@ <return type="float" /> <param index="0" name="link" type="RID" /> <description> - Returns the [code]enter_cost[/code] of this [code]link[/code]. + Returns the enter cost of this [param link]. </description> </method> <method name="link_get_map" qualifiers="const"> @@ -197,7 +197,7 @@ <return type="float" /> <param index="0" name="link" type="RID" /> <description> - Returns the [code]travel_cost[/code] of this [code]link[/code]. + Returns the travel cost of this [param link]. </description> </method> <method name="link_is_bidirectional" qualifiers="const"> @@ -477,7 +477,7 @@ <return type="float" /> <param index="0" name="region" type="RID" /> <description> - Returns the [code]enter_cost[/code] of this [param region]. + Returns the enter cost of this [param region]. </description> </method> <method name="region_get_map" qualifiers="const"> @@ -505,7 +505,7 @@ <return type="float" /> <param index="0" name="region" type="RID" /> <description> - Returns the [code]travel_cost[/code] of this [param region]. + Returns the travel cost of this [param region]. </description> </method> <method name="region_owns_point" qualifiers="const"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 5f6a679b30..b321a1f821 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2174,6 +2174,9 @@ Enables the use of physically based units for light sources. Physically based units tend to be much larger than the arbitrary units used by Godot, but they can be used to match lighting within Godot to real-world lighting. Due to the large dynamic range of lighting conditions present in nature, Godot bakes exposure into the various lighting quantities before rendering. Most light sources bake exposure automatically at run time based on the active [CameraAttributes] resource, but [LightmapGI] and [VoxelGI] require a [CameraAttributes] resource to be set at bake time to reduce the dynamic range. At run time, Godot will automatically reconcile the baked exposure with the active exposure to ensure lighting remains consistent. </member> <member name="rendering/limits/cluster_builder/max_clustered_elements" type="float" setter="" getter="" default="512"> + The maximum number of clustered elements ([OmniLight3D] + [SpotLight3D] + [Decal] + [ReflectionProbe]) that can be rendered at once in the camera view. If there are more clustered elements present in the camera view, some of them will not be rendered (leading to pop-in during camera movement). Enabling distance fade on lights and decals ([member Light3D.distance_fade_enabled], [member Decal.distance_fade_enabled]) can help avoid reaching this limit. + Decreasing this value may improve GPU performance on certain setups, even if the maximum number of clustered elements is never reached in the project. + [b]Note:[/b] This setting is only effective when using the Forward+ rendering method, not Mobile and Compatibility. </member> <member name="rendering/limits/forward_renderer/threaded_render_minimum_instances" type="int" setter="" getter="" default="500"> </member> diff --git a/doc/classes/ShapeCast3D.xml b/doc/classes/ShapeCast3D.xml index 735b91cee9..907ae73055 100644 --- a/doc/classes/ShapeCast3D.xml +++ b/doc/classes/ShapeCast3D.xml @@ -14,7 +14,7 @@ <methods> <method name="add_exception"> <return type="void" /> - <param index="0" name="node" type="Object" /> + <param index="0" name="node" type="CollisionObject3D" /> <description> Adds a collision exception so the shape does not report collisions with the specified [CollisionObject3D] node. </description> @@ -108,7 +108,7 @@ </method> <method name="remove_exception"> <return type="void" /> - <param index="0" name="node" type="Object" /> + <param index="0" name="node" type="CollisionObject3D" /> <description> Removes a collision exception so the shape does report collisions with the specified [CollisionObject3D] node. </description> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index a39a43be4c..ec2c7860d8 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -300,7 +300,7 @@ <param index="1" name="coords_from" type="Vector2i" /> <param index="2" name="alternative_from" type="int" /> <description> - Returns if there is and alternative-level proxy for the given identifiers. + Returns if there is an alternative-level proxy for the given identifiers. </description> </method> <method name="has_coords_level_tile_proxy"> diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 50826f572a..b4ef719ab0 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -1015,6 +1015,19 @@ void EditorFileDialog::add_filter(const String &p_filter, const String &p_descri invalidate(); } +void EditorFileDialog::set_filters(const Vector<String> &p_filters) { + if (filters == p_filters) { + return; + } + filters = p_filters; + update_filters(); + invalidate(); +} + +Vector<String> EditorFileDialog::get_filters() const { + return filters; +} + String EditorFileDialog::get_current_dir() const { return dir_access->get_current_dir(); } @@ -1570,6 +1583,8 @@ void EditorFileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_filters"), &EditorFileDialog::clear_filters); ClassDB::bind_method(D_METHOD("add_filter", "filter", "description"), &EditorFileDialog::add_filter, DEFVAL("")); + ClassDB::bind_method(D_METHOD("set_filters", "filters"), &EditorFileDialog::set_filters); + ClassDB::bind_method(D_METHOD("get_filters"), &EditorFileDialog::get_filters); ClassDB::bind_method(D_METHOD("get_current_dir"), &EditorFileDialog::get_current_dir); ClassDB::bind_method(D_METHOD("get_current_file"), &EditorFileDialog::get_current_file); ClassDB::bind_method(D_METHOD("get_current_path"), &EditorFileDialog::get_current_path); @@ -1579,6 +1594,7 @@ void EditorFileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("set_file_mode", "mode"), &EditorFileDialog::set_file_mode); ClassDB::bind_method(D_METHOD("get_file_mode"), &EditorFileDialog::get_file_mode); ClassDB::bind_method(D_METHOD("get_vbox"), &EditorFileDialog::get_vbox); + ClassDB::bind_method(D_METHOD("get_line_edit"), &EditorFileDialog::get_line_edit); ClassDB::bind_method(D_METHOD("set_access", "access"), &EditorFileDialog::set_access); ClassDB::bind_method(D_METHOD("get_access"), &EditorFileDialog::get_access); ClassDB::bind_method(D_METHOD("set_show_hidden_files", "show"), &EditorFileDialog::set_show_hidden_files); @@ -1605,6 +1621,7 @@ void EditorFileDialog::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir", PROPERTY_HINT_DIR, "", PROPERTY_USAGE_NONE), "set_current_dir", "get_current_dir"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file", PROPERTY_HINT_FILE, "*", PROPERTY_USAGE_NONE), "set_current_file", "get_current_file"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_path", "get_current_path"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "filters"), "set_filters", "get_filters"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_overwrite_warning"), "set_disable_overwrite_warning", "is_overwrite_warning_disabled"); diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index 1944cacf70..e3515f8073 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -242,6 +242,8 @@ public: void popup_file_dialog(); void clear_filters(); void add_filter(const String &p_filter, const String &p_description = ""); + void set_filters(const Vector<String> &p_filters); + Vector<String> get_filters() const; void set_enable_multiple_selection(bool p_enable); Vector<String> get_selected_files() const; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index cf1c1016fa..6fcf092834 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3526,7 +3526,7 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, // Only try to load the script if it has a name. Else, the plugin has no init script. if (script_path.length() > 0) { script_path = addon_path.get_base_dir().path_join(script_path); - scr = ResourceLoader::load(script_path); + scr = ResourceLoader::load(script_path, "Script", ResourceFormatLoader::CACHE_MODE_IGNORE); if (scr.is_null()) { show_warning(vformat(TTR("Unable to load addon script from path: '%s'."), script_path)); @@ -3984,6 +3984,15 @@ HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node * return modified_property_map; } +void EditorNode::update_ownership_table_for_addition_node_ancestors(Node *p_current_node, HashMap<Node *, Node *> &p_ownership_table) { + p_ownership_table.insert(p_current_node, p_current_node->get_owner()); + + for (int i = 0; i < p_current_node->get_child_count(); i++) { + Node *child = p_current_node->get_child(i); + update_ownership_table_for_addition_node_ancestors(child, p_ownership_table); + } +} + void EditorNode::update_diff_data_for_node( Node *p_edited_scene, Node *p_root, @@ -4079,6 +4088,16 @@ void EditorNode::update_diff_data_for_node( if (node_3d) { new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent()); } + + // Gathers the ownership of all ancestor nodes for later use. + HashMap<Node *, Node *> ownership_table; + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + update_ownership_table_for_addition_node_ancestors(child, ownership_table); + } + + new_additive_node_entry.ownership_table = ownership_table; + p_addition_list.push_back(new_additive_node_entry); return; @@ -6203,6 +6222,18 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins node_3d->set_transform(additive_node_entry.transform_3d); } } + + // Restore the ownership of its ancestors + for (KeyValue<Node *, Node *> &E : additive_node_entry.ownership_table) { + Node *current_ancestor = E.key; + Node *ancestor_owner = E.value; + + if (ancestor_owner == original_node) { + ancestor_owner = instantiated_node; + } + + current_ancestor->set_owner(ancestor_owner); + } } // Restore the selection. diff --git a/editor/editor_node.h b/editor/editor_node.h index eefe45ca1f..8ad5969249 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -832,6 +832,8 @@ public: // Used if the original parent node is lost Transform2D transform_2d; Transform3D transform_3d; + // Used to keep track of the ownership of all ancestor nodes so they can be restored later. + HashMap<Node *, Node *> ownership_table; }; struct ConnectionWithNodePath { @@ -846,6 +848,8 @@ public: List<Node::GroupInfo> groups; }; + void update_ownership_table_for_addition_node_ancestors(Node *p_current_node, HashMap<Node *, Node *> &p_ownership_table); + void update_diff_data_for_node( Node *p_edited_scene, Node *p_root, diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index db4d12c761..6eef4e5a5a 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -424,8 +424,10 @@ void EditorResourcePreview::check_for_invalidation(const String &p_path) { } void EditorResourcePreview::start() { - ERR_FAIL_COND_MSG(thread.is_started(), "Thread already started."); - thread.start(_thread_func, this); + if (DisplayServer::get_singleton()->get_name() != "headless") { + ERR_FAIL_COND_MSG(thread.is_started(), "Thread already started."); + thread.start(_thread_func, this); + } } void EditorResourcePreview::stop() { diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 41d175fcd1..037bfbfd99 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -746,7 +746,16 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // TRANSLATORS: Project Manager here refers to the tool used to create/manage Godot projects. EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "project_manager/sorting_order", 0, "Last Edited,Name,Path") + +#if defined(WEB_ENABLED) + // Web platform only supports `gl_compatibility`. + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_NONE, "project_manager/default_renderer", "gl_compatibility", "forward_plus,mobile,gl_compatibility") +#elif defined(ANDROID_ENABLED) + // Use more suitable rendering method by default. + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_NONE, "project_manager/default_renderer", "mobile", "forward_plus,mobile,gl_compatibility") +#else EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_NONE, "project_manager/default_renderer", "forward_plus", "forward_plus,mobile,gl_compatibility") +#endif if (p_extra_config.is_valid()) { if (p_extra_config->has_section("init_projects") && p_extra_config->has_section_key("init_projects", "list")) { diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp index 7f934bc45b..f62b22881c 100644 --- a/editor/export/editor_export_platform_pc.cpp +++ b/editor/export/editor_export_platform_pc.cpp @@ -59,11 +59,10 @@ void EditorExportPlatformPC::get_export_options(List<ExportOption> *r_options) { r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "binary_format/embed_pck"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/bptc"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/bptc"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/no_bptc_fallbacks"), true)); } String EditorExportPlatformPC::get_name() const { @@ -242,11 +241,6 @@ void EditorExportPlatformPC::get_platform_features(List<String> *r_features) con } void EditorExportPlatformPC::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) { - if (p_features.has("bptc")) { - if (p_preset->has("texture_format/no_bptc_fallbacks")) { - p_features.erase("s3tc"); - } - } } int EditorExportPlatformPC::get_chmod_flags() const { diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 32c16255dd..0ee86c8ed0 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -353,13 +353,12 @@ static String _fixstr(const String &p_what, const String &p_str) { static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r_shape_list, bool p_convex) { ERR_FAIL_NULL_MSG(mesh, "Cannot generate shape list with null mesh value"); - ERR_FAIL_NULL_MSG(mesh->get_mesh(), "Cannot generate shape list with null mesh value"); if (!p_convex) { Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape(); r_shape_list.push_back(shape); } else { Vector<Ref<Shape3D>> cd; - cd.push_back(mesh->get_mesh()->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false)); + cd.push_back(mesh->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false)); if (cd.size()) { for (int i = 0; i < cd.size(); i++) { r_shape_list.push_back(cd[i]); @@ -1230,7 +1229,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< shapes = collision_map[m]; } else { shapes = get_collision_shapes( - m->get_mesh(), + m, node_settings, p_applied_root_scale); } @@ -1613,7 +1612,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.normalize_mesh)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), static_cast<int>(decomposition_default.mode))); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.convexhull_approximation)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.max_convex_hulls)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.max_convex_hulls)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.project_hull_vertices)); // Primitives: Box, Sphere, Cylinder, Capsule. diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index d6d83a45d3..520ccd1070 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -39,6 +39,7 @@ #include "scene/resources/box_shape_3d.h" #include "scene/resources/capsule_shape_3d.h" #include "scene/resources/cylinder_shape_3d.h" +#include "scene/resources/importer_mesh.h" #include "scene/resources/mesh.h" #include "scene/resources/shape_3d.h" #include "scene/resources/sphere_shape_3d.h" @@ -298,7 +299,7 @@ public: ResourceImporterScene(bool p_animation_import = false); template <class M> - static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options, float p_applied_root_scale); + static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<ImporterMesh> &p_mesh, const M &p_options, float p_applied_root_scale); template <class M> static Transform3D get_collision_shapes_transform(const M &p_options); @@ -314,7 +315,8 @@ public: }; template <class M> -Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options, float p_applied_root_scale) { +Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<ImporterMesh> &p_mesh, const M &p_options, float p_applied_root_scale) { + ERR_FAIL_COND_V(p_mesh.is_null(), Vector<Ref<Shape3D>>()); ShapeType generate_shape_type = SHAPE_TYPE_DECOMPOSE_CONVEX; if (p_options.has(SNAME("physics/shape_type"))) { generate_shape_type = (ShapeType)p_options[SNAME("physics/shape_type")].operator int(); @@ -373,7 +375,7 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> } if (p_options.has(SNAME("decomposition/max_convex_hulls"))) { - decomposition_settings.max_convex_hulls = p_options[SNAME("decomposition/max_convex_hulls")]; + decomposition_settings.max_convex_hulls = MAX(1, (int)p_options[SNAME("decomposition/max_convex_hulls")]); } if (p_options.has(SNAME("decomposition/project_hull_vertices"))) { @@ -410,6 +412,8 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> box.instantiate(); if (p_options.has(SNAME("primitive/size"))) { box->set_size(p_options[SNAME("primitive/size")].operator Vector3() * p_applied_root_scale); + } else { + box->set_size(Vector3(2, 2, 2) * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; @@ -421,6 +425,8 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> sphere.instantiate(); if (p_options.has(SNAME("primitive/radius"))) { sphere->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale); + } else { + sphere->set_radius(1.0f * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; @@ -431,9 +437,13 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> cylinder.instantiate(); if (p_options.has(SNAME("primitive/height"))) { cylinder->set_height(p_options[SNAME("primitive/height")].operator float() * p_applied_root_scale); + } else { + cylinder->set_height(1.0f * p_applied_root_scale); } if (p_options.has(SNAME("primitive/radius"))) { cylinder->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale); + } else { + cylinder->set_radius(1.0f * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; @@ -444,9 +454,13 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> capsule.instantiate(); if (p_options.has(SNAME("primitive/height"))) { capsule->set_height(p_options[SNAME("primitive/height")].operator float() * p_applied_root_scale); + } else { + capsule->set_height(1.0f * p_applied_root_scale); } if (p_options.has(SNAME("primitive/radius"))) { capsule->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale); + } else { + capsule->set_radius(1.0f * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 348aad1162..8471e0d8a3 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -444,9 +444,45 @@ void SceneImportSettings::_update_view_gizmos() { collider_view->set_visible(show_collider_view); if (generate_collider) { // This collider_view doesn't have a mesh so we need to generate a new one. + Ref<ImporterMesh> mesh; + mesh.instantiate(); + // ResourceImporterScene::get_collision_shapes() expects ImporterMesh, not Mesh. + // TODO: Duplicate code with EditorSceneFormatImporterESCN::import_scene() + // Consider making a utility function to convert from Mesh to ImporterMesh. + Ref<Mesh> mesh_3d_mesh = mesh_node->get_mesh(); + Ref<ArrayMesh> array_mesh_3d_mesh = mesh_3d_mesh; + if (array_mesh_3d_mesh.is_valid()) { + // For the MeshInstance3D nodes, we need to convert the ArrayMesh to an ImporterMesh specially. + mesh->set_name(array_mesh_3d_mesh->get_name()); + for (int32_t blend_i = 0; blend_i < array_mesh_3d_mesh->get_blend_shape_count(); blend_i++) { + mesh->add_blend_shape(array_mesh_3d_mesh->get_blend_shape_name(blend_i)); + } + for (int32_t surface_i = 0; surface_i < array_mesh_3d_mesh->get_surface_count(); surface_i++) { + mesh->add_surface(array_mesh_3d_mesh->surface_get_primitive_type(surface_i), + array_mesh_3d_mesh->surface_get_arrays(surface_i), + array_mesh_3d_mesh->surface_get_blend_shape_arrays(surface_i), + array_mesh_3d_mesh->surface_get_lods(surface_i), + array_mesh_3d_mesh->surface_get_material(surface_i), + array_mesh_3d_mesh->surface_get_name(surface_i), + array_mesh_3d_mesh->surface_get_format(surface_i)); + } + mesh->set_blend_shape_mode(array_mesh_3d_mesh->get_blend_shape_mode()); + } else if (mesh_3d_mesh.is_valid()) { + // For the MeshInstance3D nodes, we need to convert the Mesh to an ImporterMesh specially. + mesh->set_name(mesh_3d_mesh->get_name()); + for (int32_t surface_i = 0; surface_i < mesh_3d_mesh->get_surface_count(); surface_i++) { + mesh->add_surface(mesh_3d_mesh->surface_get_primitive_type(surface_i), + mesh_3d_mesh->surface_get_arrays(surface_i), + Array(), + mesh_3d_mesh->surface_get_lods(surface_i), + mesh_3d_mesh->surface_get_material(surface_i), + mesh_3d_mesh->surface_get_material(surface_i).is_valid() ? mesh_3d_mesh->surface_get_material(surface_i)->get_name() : String(), + mesh_3d_mesh->surface_get_format(surface_i)); + } + } // Generate the mesh collider. - Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh_node->get_mesh(), e.value.settings, 1.0); + Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh, e.value.settings, 1.0); const Transform3D transform = ResourceImporterScene::get_collision_shapes_transform(e.value.settings); Ref<ArrayMesh> collider_view_mesh; @@ -558,6 +594,9 @@ void SceneImportSettings::open_settings(const String &p_path, bool p_for_animati // Visibility data_mode->set_tab_hidden(1, p_for_animation); data_mode->set_tab_hidden(2, p_for_animation); + if (p_for_animation) { + data_mode->set_current_tab(0); + } action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_EXTRACT_MATERIALS), p_for_animation); action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_CHOOSE_MESH_SAVE_PATHS), p_for_animation); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index c00ef326d0..2f3f4dd2e1 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -911,33 +911,41 @@ public: Button *rs_button = memnew(CheckBox); rs_button->set_button_group(renderer_button_group); rs_button->set_text(TTR("Forward+")); +#if defined(WEB_ENABLED) + rs_button->set_disabled(true); +#endif rs_button->set_meta(SNAME("rendering_method"), "forward_plus"); rs_button->connect("pressed", callable_mp(this, &ProjectDialog::_renderer_selected)); rvb->add_child(rs_button); if (default_renderer_type == "forward_plus") { rs_button->set_pressed(true); } - rs_button = memnew(CheckBox); rs_button->set_button_group(renderer_button_group); rs_button->set_text(TTR("Mobile")); +#if defined(WEB_ENABLED) + rs_button->set_disabled(true); +#endif rs_button->set_meta(SNAME("rendering_method"), "mobile"); rs_button->connect("pressed", callable_mp(this, &ProjectDialog::_renderer_selected)); rvb->add_child(rs_button); if (default_renderer_type == "mobile") { rs_button->set_pressed(true); } - rs_button = memnew(CheckBox); rs_button->set_button_group(renderer_button_group); rs_button->set_text(TTR("Compatibility")); +#if !defined(GLES3_ENABLED) + rs_button->set_disabled(true); +#endif rs_button->set_meta(SNAME("rendering_method"), "gl_compatibility"); rs_button->connect("pressed", callable_mp(this, &ProjectDialog::_renderer_selected)); rvb->add_child(rs_button); +#if defined(GLES3_ENABLED) if (default_renderer_type == "gl_compatibility") { rs_button->set_pressed(true); } - +#endif rshc->add_child(memnew(VSeparator)); // Right hand side, used for text explaining each choice. diff --git a/main/main.cpp b/main/main.cpp index 5bb37df3ca..b15588e700 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1131,6 +1131,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph cmdline_tool = true; main_args.push_back(I->get()); #ifndef DISABLE_DEPRECATED + } else if (I->get() == "--export") { // For users used to 3.x syntax. + OS::get_singleton()->print("The Godot 3 --export option was changed to more explicit --export-release / --export-debug / --export-pack options.\nSee the --help output for details.\n"); + goto error; } else if (I->get() == "--convert-3to4") { // Actually handling is done in start(). cmdline_tool = true; diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj index 16e58172b2..09e8bf4cc7 100644 --- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj +++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj @@ -14,7 +14,6 @@ 1FF8DBB11FBA9DE1009DE660 /* dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */; }; D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07CD44D1C5D589C00B7FB28 /* Images.xcassets */; }; 9039D3BE24C093AC0020482C /* MoltenVK.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9039D3BD24C093AC0020482C /* MoltenVK.xcframework */; }; - 9073252C24BF980B0063BCD4 /* vulkan in Resources */ = {isa = PBXBuildFile; fileRef = 905036DC24BF932E00301046 /* vulkan */; }; D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE4418AEBDA2004A7AAE /* InfoPlist.strings */; }; D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE7718AEBFEB004A7AAE /* $binary.pck */; }; $pbx_launch_screen_build_reference @@ -42,7 +41,6 @@ 1FF4C1881F584E6300A41E41 /* $binary.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "$binary.entitlements"; sourceTree = "<group>"; }; 1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = "<group>"; }; 9039D3BD24C093AC0020482C /* MoltenVK.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MoltenVK; path = MoltenVK.xcframework; sourceTree = "<group>"; }; - 905036DC24BF932E00301046 /* vulkan */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vulkan; path = "$binary/vulkan"; sourceTree = "<group>"; }; D07CD44D1C5D589C00B7FB28 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; D0BCFE3418AEBDA2004A7AAE /* $binary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "$binary.app"; sourceTree = BUILT_PRODUCTS_DIR; }; D0BCFE4318AEBDA2004A7AAE /* $binary-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "$binary-Info.plist"; sourceTree = "<group>"; }; @@ -72,7 +70,6 @@ D0BCFE2B18AEBDA2004A7AAE = { isa = PBXGroup; children = ( - 905036DC24BF932E00301046 /* vulkan */, 1F1575711F582BE20003B888 /* dylibs */, D0BCFE7718AEBFEB004A7AAE /* $binary.pck */, D0BCFE4118AEBDA2004A7AAE /* $binary */, @@ -185,7 +182,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9073252C24BF980B0063BCD4 /* vulkan in Resources */, D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */, D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */, $pbx_launch_screen_build_phase diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index f19228cb18..ad70772270 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -30,8 +30,8 @@ #include "image_compress_cvtt.h" +#include "core/object/worker_thread_pool.h" #include "core/os/os.h" -#include "core/os/thread.h" #include "core/string/print_string.h" #include "core/templates/safe_refcount.h" @@ -129,6 +129,18 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const } } +static void _digest_job_queue(void *p_job_queue, uint32_t p_index) { + CVTTCompressionJobQueue *job_queue = static_cast<CVTTCompressionJobQueue *>(p_job_queue); + uint32_t num_tasks = job_queue->num_tasks; + uint32_t total_threads = WorkerThreadPool::get_singleton()->get_thread_count(); + uint32_t start = p_index * num_tasks / total_threads; + uint32_t end = (p_index + 1 == total_threads) ? num_tasks : ((p_index + 1) * num_tasks / total_threads); + + for (uint32_t i = start; i < end; i++) { + _digest_row_task(job_queue->job_params, job_queue->job_tasks[i]); + } +} + void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) { if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) { return; //do not compress, already compressed @@ -220,7 +232,7 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) { row_task.in_mm_bytes = in_bytes; row_task.out_mm_bytes = out_bytes; - _digest_row_task(job_queue.job_params, row_task); + tasks.push_back(row_task); out_bytes += 16 * (bw / 4); } @@ -230,6 +242,13 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) { h = MAX(h / 2, 1); } + const CVTTCompressionRowTask *tasks_rb = tasks.ptr(); + + job_queue.job_tasks = &tasks_rb[0]; + job_queue.num_tasks = static_cast<uint32_t>(tasks.size()); + WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&_digest_job_queue, &job_queue, WorkerThreadPool::get_singleton()->get_thread_count(), -1, true, SNAME("CVTT Compress")); + WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); + p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 08c8763493..045505b720 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -126,6 +126,20 @@ <param index="0" name="value" type="Variant" /> <param index="1" name="type" type="Variant" /> <description> + Returns [code]true[/code] if [param value] is an instance of [param type]. The [param type] value must be one of the following: + - A constant from the [enum Variant.Type] enumeration, for example [constant TYPE_INT]. + - An [Object]-derived class which exists in [ClassDB], for example [Node]. + - A [Script] (you can use any class, including inner one). + Unlike the right operand of the [code]is[/code] operator, [param type] can be a non-constant value. The [code]is[/code] operator supports more features (such as typed arrays) and is more performant. Use the operator instead of this method if you do not need dynamic type checking. + Examples: + [codeblock] + print(is_instance_of(a, TYPE_INT)) + print(is_instance_of(a, Node)) + print(is_instance_of(a, MyClass)) + print(is_instance_of(a, MyClass.InnerClass)) + [/codeblock] + [b]Note:[/b] If [param value] and/or [param type] are freed objects (see [method @GlobalScope.is_instance_valid]), or [param type] is not one of the above options, this method will raise an runtime error. + See also [method @GlobalScope.typeof], [method type_exists], [method Array.is_same_typed] (and other [Array] methods). </description> </method> <method name="len"> diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 5ce01a08bf..e058187860 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1857,33 +1857,40 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { push_error(vformat(R"*(Invalid call for "range()" function. Expected at most 3 arguments, %d given.)*", call->arguments.size()), call->callee); } else { // Now we can optimize it. - bool all_is_constant = true; + bool can_reduce = true; Vector<Variant> args; args.resize(call->arguments.size()); for (int i = 0; i < call->arguments.size(); i++) { - reduce_expression(call->arguments[i]); + GDScriptParser::ExpressionNode *argument = call->arguments[i]; + reduce_expression(argument); - if (!call->arguments[i]->is_constant) { - all_is_constant = false; - } else if (all_is_constant) { - args.write[i] = call->arguments[i]->reduced_value; - } - - GDScriptParser::DataType arg_type = call->arguments[i]->get_datatype(); - if (!arg_type.is_variant()) { - if (arg_type.kind != GDScriptParser::DataType::BUILTIN) { - all_is_constant = false; - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); - } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) { - all_is_constant = false; - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); + if (argument->is_constant) { + if (argument->reduced_value.get_type() != Variant::INT && argument->reduced_value.get_type() != Variant::FLOAT) { + can_reduce = false; + push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, Variant::get_type_name(argument->reduced_value.get_type())), argument); + } + if (can_reduce) { + args.write[i] = argument->reduced_value; + } + } else { + can_reduce = false; + GDScriptParser::DataType argument_type = argument->get_datatype(); + if (argument_type.is_variant() || !argument_type.is_hard_type()) { + mark_node_unsafe(argument); + } + if (!argument_type.is_variant() && (argument_type.builtin_type != Variant::INT && argument_type.builtin_type != Variant::FLOAT)) { + if (!argument_type.is_hard_type()) { + downgrade_node_type_source(argument); + } else { + push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, argument_type.to_string()), argument); + } } } } Variant reduced; - if (all_is_constant) { + if (can_reduce) { switch (args.size()) { case 1: reduced = (int32_t)args[0]; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 35c9946bc1..fad2bf334b 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -211,6 +211,10 @@ static bool _can_use_ptrcall(const MethodBind *p_method, const Vector<GDScriptCo return true; } +inline static bool is_category_or_group(const PropertyInfo &p_info) { + return p_info.usage & PROPERTY_USAGE_CATEGORY || p_info.usage & PROPERTY_USAGE_GROUP || p_info.usage & PROPERTY_USAGE_SUBGROUP; +} + GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) { if (p_expression->is_constant && !(p_expression->get_datatype().is_meta_type && p_expression->get_datatype().kind == GDScriptParser::DataType::CLASS)) { return codegen.add_constant(p_expression->reduced_value); @@ -246,7 +250,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Try members. if (!codegen.function_node || !codegen.function_node->is_static) { // Try member variables. - if (codegen.script->member_indices.has(identifier)) { + if (codegen.script->member_indices.has(identifier) && !is_category_or_group(codegen.script->member_info[identifier])) { if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) { // Perform getter. GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index b32313dad4..c402b63f7b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3138,6 +3138,14 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p bool previous_in_lambda = in_lambda; in_lambda = true; + // Save break/continue state. + bool could_break = can_break; + bool could_continue = can_continue; + + // Disallow break/continue. + can_break = false; + can_continue = false; + function->body = parse_suite("lambda declaration", body, true); complete_extents(function); complete_extents(lambda); @@ -3155,6 +3163,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p current_function = previous_function; in_lambda = previous_in_lambda; lambda->function = function; + + // Reset break/continue state. + can_break = could_break; + can_continue = could_continue; + return lambda; } diff --git a/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd new file mode 100644 index 0000000000..d2d9d04508 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd @@ -0,0 +1,9 @@ +enum E { E0 = 0, E3 = 3 } + +func test(): + var total := 0 + for value in range(E.E0, E.E3): + var inferable := value + total += inferable + assert(total == 0 + 1 + 2) + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.gd new file mode 100644 index 0000000000..4a7f10f1ee --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.gd @@ -0,0 +1,7 @@ +func test(): + var array := [3, 6, 9] + var result := '' + for i in range(array.size(), 0, -1): + result += str(array[i - 1]) + assert(result == '963') + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.out b/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd new file mode 100644 index 0000000000..319c1801c0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd @@ -0,0 +1,5 @@ +func test(): + for index in range(0, 1): + var lambda := func(): + continue + print('not ok') diff --git a/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out new file mode 100644 index 0000000000..262dfbc09b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Cannot use "continue" outside of a loop. diff --git a/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd new file mode 100644 index 0000000000..2fa45c1d7d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd @@ -0,0 +1,13 @@ +func test(): + var i_string := '' + for i in 3: + if i == 1: continue + var lambda := func(): + var j_string := '' + for j in 3: + if j == 1: continue + j_string += str(j) + return j_string + i_string += lambda.call() + assert(i_string == '0202') + print('ok') diff --git a/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/runtime/features/groups_are_not_properties.gd b/modules/gdscript/tests/scripts/runtime/features/groups_are_not_properties.gd new file mode 100644 index 0000000000..d205da22c2 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/groups_are_not_properties.gd @@ -0,0 +1,9 @@ +# https://github.com/godotengine/godot/issues/73843 +extends RefCounted + +@export_group("Resource") +@export_category("RefCounted") + +func test(): + prints("Not shadowed", Resource.new()) + prints("Not shadowed", RefCounted.new()) diff --git a/modules/gdscript/tests/scripts/runtime/features/groups_are_not_properties.out b/modules/gdscript/tests/scripts/runtime/features/groups_are_not_properties.out new file mode 100644 index 0000000000..a1a2ff6cd9 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/groups_are_not_properties.out @@ -0,0 +1,3 @@ +GDTEST_OK +Not shadowed <Resource#-9223371975785708326> +Not shadowed <RefCounted#-9223371975768931110> diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 9a7b37df21..214d4d8ec2 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -913,7 +913,7 @@ void GridMapEditor::update_palette() { } void GridMapEditor::edit(GridMap *p_gridmap) { - if (!p_gridmap && node) { + if (node) { node->disconnect("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids)); } diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 86b0e04206..8bcfc5a5eb 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -271,7 +271,12 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = initialize_hostfxr_for_config(get_data(config_path)); - ERR_FAIL_NULL_V(load_assembly_and_get_function_pointer, nullptr); + + if (load_assembly_and_get_function_pointer == nullptr) { + // Show a message box to the user to make the problem explicit (and explain a potential crash). + OS::get_singleton()->alert(TTR("Unable to load .NET runtime, no compatible version was found.\nAttempting to create/edit a project will lead to a crash.\n\nPlease install the .NET SDK 6.0 or later from https://dotnet.microsoft.com/en-us/download and restart Godot."), TTR("Failed to load .NET runtime")); + ERR_FAIL_V_MSG(nullptr, ".NET: Failed to load compatible .NET runtime"); + } r_runtime_initialized = true; @@ -394,6 +399,9 @@ void GDMono::initialize() { ERR_FAIL_MSG(".NET: Failed to load hostfxr"); } #else + + // Show a message box to the user to make the problem explicit (and explain a potential crash). + OS::get_singleton()->alert(TTR("Unable to load .NET runtime, specifically hostfxr.\nAttempting to create/edit a project will lead to a crash.\n\nPlease install the .NET SDK 6.0 or later from https://dotnet.microsoft.com/en-us/download and restart Godot."), TTR("Failed to load .NET runtime")); ERR_FAIL_MSG(".NET: Failed to load hostfxr"); #endif } diff --git a/modules/multiplayer/scene_replication_config.cpp b/modules/multiplayer/scene_replication_config.cpp index c8621e7357..f8006228de 100644 --- a/modules/multiplayer/scene_replication_config.cpp +++ b/modules/multiplayer/scene_replication_config.cpp @@ -114,6 +114,8 @@ void SceneReplicationConfig::add_property(const NodePath &p_path, int p_index) { if (p_index < 0 || p_index == properties.size()) { properties.push_back(ReplicationProperty(p_path)); + sync_props.push_back(p_path); + spawn_props.push_back(p_path); return; } @@ -126,6 +128,16 @@ void SceneReplicationConfig::add_property(const NodePath &p_path, int p_index) { c++; } properties.insert_before(I, ReplicationProperty(p_path)); + sync_props.clear(); + spawn_props.clear(); + for (const ReplicationProperty &prop : properties) { + if (prop.sync) { + sync_props.push_back(p_path); + } + if (prop.spawn) { + spawn_props.push_back(p_path); + } + } } void SceneReplicationConfig::remove_property(const NodePath &p_path) { diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index c12fc5e834..9e706dbeef 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -340,6 +340,7 @@ void WebSocketMultiplayerPeer::_poll_server() { to_remove.insert(id); continue; } + peer.connection = tls; } Ref<StreamPeerTLS> tls = static_cast<Ref<StreamPeerTLS>>(peer.connection); tls->poll(); diff --git a/platform/linuxbsd/tts_linux.cpp b/platform/linuxbsd/tts_linux.cpp index 6b8584ef6c..ce0199e87f 100644 --- a/platform/linuxbsd/tts_linux.cpp +++ b/platform/linuxbsd/tts_linux.cpp @@ -35,11 +35,6 @@ TTS_Linux *TTS_Linux::singleton = nullptr; -void TTS_Linux::_bind_methods() { - ClassDB::bind_method(D_METHOD("_speech_event", "msg_id", "client_id", "type"), &TTS_Linux::_speech_event); - ClassDB::bind_method(D_METHOD("_speech_index_mark", "msg_id", "client_id", "type", "index_mark"), &TTS_Linux::_speech_index_mark); -} - void TTS_Linux::speech_init_thread_func(void *p_userdata) { TTS_Linux *tts = (TTS_Linux *)p_userdata; if (tts) { @@ -82,7 +77,7 @@ void TTS_Linux::speech_init_thread_func(void *p_userdata) { void TTS_Linux::speech_event_index_mark(size_t p_msg_id, size_t p_client_id, SPDNotificationType p_type, char *p_index_mark) { TTS_Linux *tts = TTS_Linux::get_singleton(); if (tts) { - tts->call_deferred(SNAME("_speech_index_mark"), p_msg_id, p_client_id, (int)p_type, String::utf8(p_index_mark)); + callable_mp(tts, &TTS_Linux::_speech_index_mark).call_deferred(p_msg_id, p_client_id, (int)p_type, String::utf8(p_index_mark)); } } @@ -97,7 +92,7 @@ void TTS_Linux::_speech_index_mark(size_t p_msg_id, size_t p_client_id, int p_ty void TTS_Linux::speech_event_callback(size_t p_msg_id, size_t p_client_id, SPDNotificationType p_type) { TTS_Linux *tts = TTS_Linux::get_singleton(); if (tts) { - tts->call_deferred(SNAME("_speech_event"), p_msg_id, p_client_id, (int)p_type); + callable_mp(tts, &TTS_Linux::_speech_event).call_deferred(p_msg_id, p_client_id, (int)p_type); } } diff --git a/platform/linuxbsd/tts_linux.h b/platform/linuxbsd/tts_linux.h index 3fcbe3207b..4134f8fa2f 100644 --- a/platform/linuxbsd/tts_linux.h +++ b/platform/linuxbsd/tts_linux.h @@ -46,7 +46,6 @@ #endif class TTS_Linux : public Object { - GDCLASS(TTS_Linux, Object); _THREAD_SAFE_CLASS_ List<DisplayServer::TTSUtterance> queue; @@ -65,7 +64,6 @@ class TTS_Linux : public Object { static TTS_Linux *singleton; protected: - static void _bind_methods(); void _speech_event(size_t p_msg_id, size_t p_client_id, int p_type); void _speech_index_mark(size_t p_msg_id, size_t p_client_id, int p_type, const String &p_index_mark); diff --git a/platform/linuxbsd/x11/key_mapping_x11.cpp b/platform/linuxbsd/x11/key_mapping_x11.cpp index 85db873dff..fe73162280 100644 --- a/platform/linuxbsd/x11/key_mapping_x11.cpp +++ b/platform/linuxbsd/x11/key_mapping_x11.cpp @@ -217,8 +217,8 @@ void KeyMappingX11::initialize() { scancode_map[0x1F] = Key::I; scancode_map[0x20] = Key::O; scancode_map[0x21] = Key::P; - scancode_map[0x22] = Key::BRACELEFT; - scancode_map[0x23] = Key::BRACERIGHT; + scancode_map[0x22] = Key::BRACKETLEFT; + scancode_map[0x23] = Key::BRACKETRIGHT; scancode_map[0x24] = Key::ENTER; scancode_map[0x25] = Key::CTRL; scancode_map[0x26] = Key::A; diff --git a/platform/macos/key_mapping_macos.mm b/platform/macos/key_mapping_macos.mm index 31b71ac1b8..c3f147e33f 100644 --- a/platform/macos/key_mapping_macos.mm +++ b/platform/macos/key_mapping_macos.mm @@ -98,10 +98,10 @@ void KeyMappingMacOS::initialize() { keysym_map[0x1b] = Key::MINUS; keysym_map[0x1c] = Key::KEY_8; keysym_map[0x1d] = Key::KEY_0; - keysym_map[0x1e] = Key::BRACERIGHT; + keysym_map[0x1e] = Key::BRACKETRIGHT; keysym_map[0x1f] = Key::O; keysym_map[0x20] = Key::U; - keysym_map[0x21] = Key::BRACELEFT; + keysym_map[0x21] = Key::BRACKETLEFT; keysym_map[0x22] = Key::I; keysym_map[0x23] = Key::P; keysym_map[0x24] = Key::ENTER; diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index d2889a3442..92af3fef69 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -4237,7 +4237,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win window_position = screen_get_position(p_screen) + (screen_get_size(p_screen) - p_resolution) / 2; } - WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution)); + WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution)); ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd); diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp index d43f74126d..aa393464b6 100644 --- a/platform/windows/key_mapping_windows.cpp +++ b/platform/windows/key_mapping_windows.cpp @@ -257,8 +257,8 @@ void KeyMappingWindows::initialize() { scansym_map[0x17] = Key::I; scansym_map[0x18] = Key::O; scansym_map[0x19] = Key::P; - scansym_map[0x1A] = Key::BRACELEFT; - scansym_map[0x1B] = Key::BRACERIGHT; + scansym_map[0x1A] = Key::BRACKETLEFT; + scansym_map[0x1B] = Key::BRACKETRIGHT; scansym_map[0x1C] = Key::ENTER; scansym_map[0x1D] = Key::CTRL; scansym_map[0x1E] = Key::A; diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index f7d2ae7d2d..2b90a3702f 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -414,15 +414,19 @@ Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const { } void Camera2D::_make_current(Object *p_which) { + if (!viewport || (custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + return; + } + if (p_which == this) { if (is_inside_tree()) { - get_viewport()->_camera_2d_set(this); + viewport->_camera_2d_set(this); queue_redraw(); } } else { if (is_inside_tree()) { - if (get_viewport()->get_camera_2d() == this) { - get_viewport()->_camera_2d_set(nullptr); + if (viewport->get_camera_2d() == this) { + viewport->_camera_2d_set(nullptr); } queue_redraw(); } @@ -449,7 +453,7 @@ void Camera2D::make_current() { void Camera2D::clear_current() { ERR_FAIL_COND(!is_current()); - if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) && viewport->is_inside_tree()) { viewport->assign_next_enabled_camera_2d(group_name); } } diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp index 87361d6b38..d880e422f0 100644 --- a/scene/3d/shape_cast_3d.cpp +++ b/scene/3d/shape_cast_3d.cpp @@ -437,26 +437,18 @@ void ShapeCast3D::add_exception_rid(const RID &p_rid) { exclude.insert(p_rid); } -void ShapeCast3D::add_exception(const Object *p_object) { - ERR_FAIL_NULL(p_object); - const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object); - if (!co) { - return; - } - add_exception_rid(co->get_rid()); +void ShapeCast3D::add_exception(const CollisionObject3D *p_node) { + ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D."); + add_exception_rid(p_node->get_rid()); } void ShapeCast3D::remove_exception_rid(const RID &p_rid) { exclude.erase(p_rid); } -void ShapeCast3D::remove_exception(const Object *p_object) { - ERR_FAIL_NULL(p_object); - const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object); - if (!co) { - return; - } - remove_exception_rid(co->get_rid()); +void ShapeCast3D::remove_exception(const CollisionObject3D *p_node) { + ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D."); + remove_exception_rid(p_node->get_rid()); } void ShapeCast3D::clear_exceptions() { diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h index 344f1d3b8a..98158d3c7c 100644 --- a/scene/3d/shape_cast_3d.h +++ b/scene/3d/shape_cast_3d.h @@ -34,6 +34,8 @@ #include "scene/3d/node_3d.h" #include "scene/resources/shape_3d.h" +class CollisionObject3D; + class ShapeCast3D : public Node3D { GDCLASS(ShapeCast3D, Node3D); @@ -133,9 +135,9 @@ public: bool is_colliding() const; void add_exception_rid(const RID &p_rid); - void add_exception(const Object *p_object); + void add_exception(const CollisionObject3D *p_node); void remove_exception_rid(const RID &p_rid); - void remove_exception(const Object *p_object); + void remove_exception(const CollisionObject3D *p_node); void clear_exceptions(); virtual PackedStringArray get_configuration_warnings() const override; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index b861d7af01..59f7511894 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -86,7 +86,7 @@ int Label::get_line_height(int p_line) const { } } -bool Label::_shape() { +void Label::_shape() { Ref<StyleBox> style = theme_cache.normal_style; int width = (get_size().width - style->get_minimum_size().width); @@ -101,7 +101,7 @@ bool Label::_shape() { } const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; - ERR_FAIL_COND_V(font.is_null(), true); + ERR_FAIL_COND(font.is_null()); String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { txt = txt.substr(0, visible_chars); @@ -121,7 +121,6 @@ bool Label::_shape() { dirty = false; font_dirty = false; lines_dirty = true; - // Note for future maintainers: forgetting stable width here (e.g., setting it to -1) may fix still undiscovered bugs. } if (lines_dirty) { @@ -129,143 +128,126 @@ bool Label::_shape() { TS->free_rid(lines_rid[i]); } lines_rid.clear(); - } - - Size2i prev_minsize = minsize; - minsize = Size2(); - bool can_process_lines = false; - if (xl_text.length() == 0) { - can_process_lines = true; - lines_dirty = false; - } else { - // With autowrap on or off with trimming enabled, we won't compute the minimum size until width is stable - // (two shape requests in a row with the same width.) This avoids situations in which the initial width is - // very narrow and the label would break text into many very short lines, causing a very tall label that can - // leave a deformed container. In the remaining case (namely, autowrap off and no trimming), the label is - // free to dictate its own width, something that will be taken advtantage of. - bool can_dictate_width = autowrap_mode == TextServer::AUTOWRAP_OFF && overrun_behavior == TextServer::OVERRUN_NO_TRIMMING; - bool is_width_stable = get_size().width == stable_width; - can_process_lines = can_dictate_width || is_width_stable; - stable_width = get_size().width; - - if (can_process_lines) { - if (lines_dirty) { - BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; - switch (autowrap_mode) { - case TextServer::AUTOWRAP_WORD_SMART: - autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; - break; - case TextServer::AUTOWRAP_WORD: - autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; - break; - case TextServer::AUTOWRAP_ARBITRARY: - autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; - break; - case TextServer::AUTOWRAP_OFF: - break; - } - autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; + BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; + switch (autowrap_mode) { + case TextServer::AUTOWRAP_WORD_SMART: + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; + break; + case TextServer::AUTOWRAP_WORD: + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; + break; + case TextServer::AUTOWRAP_ARBITRARY: + autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; + break; + case TextServer::AUTOWRAP_OFF: + break; + } + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); - for (int i = 0; i < line_breaks.size(); i = i + 2) { - RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); - lines_rid.push_back(line); - } - } + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); + lines_rid.push_back(line); + } + } - if (can_dictate_width) { - for (int i = 0; i < lines_rid.size(); i++) { - if (minsize.width < TS->shaped_text_get_size(lines_rid[i]).x) { - minsize.width = TS->shaped_text_get_size(lines_rid[i]).x; - } - } + if (xl_text.length() == 0) { + minsize = Size2(1, get_line_height()); + return; + } - width = (minsize.width - style->get_minimum_size().width); + if (autowrap_mode == TextServer::AUTOWRAP_OFF) { + minsize.width = 0.0f; + for (int i = 0; i < lines_rid.size(); i++) { + if (minsize.width < TS->shaped_text_get_size(lines_rid[i]).x) { + minsize.width = TS->shaped_text_get_size(lines_rid[i]).x; } + } + } - if (lines_dirty) { - BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; - switch (overrun_behavior) { - case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: - overrun_flags.set_flag(TextServer::OVERRUN_TRIM); - overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); - overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); - break; - case TextServer::OVERRUN_TRIM_ELLIPSIS: - overrun_flags.set_flag(TextServer::OVERRUN_TRIM); - overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); - break; - case TextServer::OVERRUN_TRIM_WORD: - overrun_flags.set_flag(TextServer::OVERRUN_TRIM); - overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); - break; - case TextServer::OVERRUN_TRIM_CHAR: - overrun_flags.set_flag(TextServer::OVERRUN_TRIM); - break; - case TextServer::OVERRUN_NO_TRIMMING: - break; - } + if (lines_dirty) { + BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; + switch (overrun_behavior) { + case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); + break; + case TextServer::OVERRUN_TRIM_ELLIPSIS: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); + break; + case TextServer::OVERRUN_TRIM_WORD: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); + break; + case TextServer::OVERRUN_TRIM_CHAR: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + break; + case TextServer::OVERRUN_NO_TRIMMING: + break; + } - // Fill after min_size calculation. + // Fill after min_size calculation. - int visible_lines = lines_rid.size(); - if (max_lines_visible >= 0 && visible_lines > max_lines_visible) { - visible_lines = max_lines_visible; - } - if (autowrap_mode != TextServer::AUTOWRAP_OFF) { - bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size(); - if (lines_hidden) { - overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); - } - if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { - for (int i = 0; i < lines_rid.size(); i++) { - if (i < visible_lines - 1 || lines_rid.size() == 1) { - TS->shaped_text_fit_to_width(lines_rid[i], width); - } else if (i == (visible_lines - 1)) { - TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); - } - } - } else if (lines_hidden) { + if (autowrap_mode != TextServer::AUTOWRAP_OFF) { + int visible_lines = get_visible_line_count(); + bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size(); + if (lines_hidden) { + overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); + } + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + for (int i = 0; i < lines_rid.size(); i++) { + if (i < visible_lines - 1 || lines_rid.size() == 1) { + TS->shaped_text_fit_to_width(lines_rid[i], width); + } else if (i == (visible_lines - 1)) { TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); } - } else { - // Autowrap disabled. - for (int i = 0; i < lines_rid.size(); i++) { - if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { - TS->shaped_text_fit_to_width(lines_rid[i], width); - overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); - TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); - TS->shaped_text_fit_to_width(lines_rid[i], width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); - } else { - TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); - } - } - } - - int last_line = MIN(lines_rid.size(), visible_lines + lines_skipped); - int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing; - for (int64_t i = lines_skipped; i < last_line; i++) { - minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; } - - lines_dirty = false; + } else if (lines_hidden) { + TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); } } else { - callable_mp(this, &Label::_shape).call_deferred(); + // Autowrap disabled. + for (int i = 0; i < lines_rid.size(); i++) { + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + TS->shaped_text_fit_to_width(lines_rid[i], width); + overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); + TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); + TS->shaped_text_fit_to_width(lines_rid[i], width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); + } else { + TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); + } + } } + lines_dirty = false; } - if (draw_pending) { - queue_redraw(); - draw_pending = false; - } + _update_visible(); - if (minsize != prev_minsize) { + if (autowrap_mode == TextServer::AUTOWRAP_OFF || !clip || overrun_behavior == TextServer::OVERRUN_NO_TRIMMING) { update_minimum_size(); } +} + +void Label::_update_visible() { + int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing; + Ref<StyleBox> style = theme_cache.normal_style; + int lines_visible = lines_rid.size(); - return can_process_lines; + if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { + lines_visible = max_lines_visible; + } + + minsize.height = 0; + int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); + for (int64_t i = lines_skipped; i < last_line; i++) { + minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; + if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) { + break; + } + } } inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) { @@ -375,11 +357,7 @@ void Label::_notification(int p_what) { } if (dirty || font_dirty || lines_dirty) { - if (!_shape()) { - // There will be another pass. - draw_pending = true; - break; - } + _shape(); } RID ci = get_canvas_item(); @@ -622,8 +600,6 @@ void Label::_notification(int p_what) { } ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing; } - - draw_pending = false; } break; case NOTIFICATION_THEME_CHANGED: { @@ -915,7 +891,7 @@ void Label::set_lines_skipped(int p_lines) { } lines_skipped = p_lines; - lines_dirty = true; + _update_visible(); queue_redraw(); } @@ -929,7 +905,7 @@ void Label::set_max_lines_visible(int p_lines) { } max_lines_visible = p_lines; - lines_dirty = true; + _update_visible(); queue_redraw(); } diff --git a/scene/gui/label.h b/scene/gui/label.h index 36b85f7af8..2350525236 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -46,7 +46,6 @@ private: bool clip = false; TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING; Size2 minsize; - real_t stable_width = -1; bool uppercase = false; bool lines_dirty = true; @@ -65,7 +64,6 @@ private: float visible_ratio = 1.0; int lines_skipped = 0; int max_lines_visible = -1; - bool draw_pending = false; Ref<LabelSettings> settings; @@ -83,7 +81,8 @@ private: int font_shadow_outline_size; } theme_cache; - bool _shape(); + void _update_visible(); + void _shape(); void _invalidate(); protected: diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 672581bbe2..0fc72ca90f 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -31,6 +31,7 @@ #include "importer_mesh.h" #include "core/io/marshalls.h" +#include "core/math/convex_hull.h" #include "core/math/random_pcg.h" #include "core/math/static_raycaster.h" #include "scene/resources/surface_tool.h" @@ -984,6 +985,43 @@ Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecomposit return ret; } +Ref<ConvexPolygonShape3D> ImporterMesh::create_convex_shape(bool p_clean, bool p_simplify) const { + if (p_simplify) { + Mesh::ConvexDecompositionSettings settings; + settings.max_convex_hulls = 1; + Vector<Ref<Shape3D>> decomposed = convex_decompose(settings); + if (decomposed.size() == 1) { + return decomposed[0]; + } else { + ERR_PRINT("Convex shape simplification failed, falling back to simpler process."); + } + } + + Vector<Vector3> vertices; + for (int i = 0; i < get_surface_count(); i++) { + Array a = get_surface_arrays(i); + ERR_FAIL_COND_V(a.is_empty(), Ref<ConvexPolygonShape3D>()); + Vector<Vector3> v = a[Mesh::ARRAY_VERTEX]; + vertices.append_array(v); + } + + Ref<ConvexPolygonShape3D> shape = memnew(ConvexPolygonShape3D); + + if (p_clean) { + Geometry3D::MeshData md; + Error err = ConvexHullComputer::convex_hull(vertices, md); + if (err == OK) { + shape->set_points(md.vertices); + return shape; + } else { + ERR_PRINT("Convex shape cleaning failed, falling back to simpler process."); + } + } + + shape->set_points(vertices); + return shape; +} + Ref<ConcavePolygonShape3D> ImporterMesh::create_trimesh_shape() const { Vector<Face3> faces = get_faces(); if (faces.size() == 0) { diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h index 900a8851c0..33d0864342 100644 --- a/scene/resources/importer_mesh.h +++ b/scene/resources/importer_mesh.h @@ -119,6 +119,7 @@ public: Vector<Face3> get_faces() const; Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const; + Ref<ConvexPolygonShape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const; Ref<ConcavePolygonShape3D> create_trimesh_shape() const; Ref<NavigationMesh> create_navigation_mesh(); Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache); diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp index 9389f8149e..96e3683560 100644 --- a/servers/rendering/renderer_rd/effects/ss_effects.cpp +++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp @@ -572,7 +572,15 @@ void SSEffects::gather_ssil(RD::ComputeListID p_compute_list, const RID *p_ssil_ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, uniform_set_cache->get_cache(shader, 2, u_ssil_slice, u_edges_slice), 2); RD::get_singleton()->compute_list_set_push_constant(p_compute_list, &ssil.gather_push_constant, sizeof(SSILGatherPushConstant)); - Size2i size = Size2i(p_settings.full_screen_size.x >> (ssil_half_size ? 2 : 1), p_settings.full_screen_size.y >> (ssil_half_size ? 2 : 1)); + Size2i size; + // Calculate size same way as we created the buffer + if (ssil_half_size) { + size.x = (p_settings.full_screen_size.x + 3) / 4; + size.y = (p_settings.full_screen_size.y + 3) / 4; + } else { + size.x = (p_settings.full_screen_size.x + 1) / 2; + size.y = (p_settings.full_screen_size.y + 1) / 2; + } RD::get_singleton()->compute_list_dispatch_threads(p_compute_list, size.x, size.y, 1); } @@ -887,8 +895,9 @@ void SSEffects::screen_space_indirect_lighting(Ref<RenderSceneBuffersRD> p_rende RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssil.blur_push_constant, sizeof(SSILBlurPushConstant)); - int x_groups = (p_settings.full_screen_size.x >> (ssil_half_size ? 2 : 1)); - int y_groups = (p_settings.full_screen_size.y >> (ssil_half_size ? 2 : 1)); + // Use the size of the actual buffer we're processing here or we won't cover the entire image. + int x_groups = p_ssil_buffers.buffer_width; + int y_groups = p_ssil_buffers.buffer_height; RD::get_singleton()->compute_list_dispatch_threads(compute_list, x_groups, y_groups, 1); if (ssil_quality > RS::ENV_SSIL_QUALITY_VERY_LOW) { @@ -985,7 +994,15 @@ void SSEffects::gather_ssao(RD::ComputeListID p_compute_list, const RID *p_ao_sl RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, uniform_set_cache->get_cache(shader, 2, u_ao_slice), 2); RD::get_singleton()->compute_list_set_push_constant(p_compute_list, &ssao.gather_push_constant, sizeof(SSAOGatherPushConstant)); - Size2i size = Size2i(p_settings.full_screen_size.x >> (ssao_half_size ? 2 : 1), p_settings.full_screen_size.y >> (ssao_half_size ? 2 : 1)); + Size2i size; + // Make sure we use the same size as with which our buffer was created + if (ssao_half_size) { + size.x = (p_settings.full_screen_size.x + 3) / 4; + size.y = (p_settings.full_screen_size.y + 3) / 4; + } else { + size.x = (p_settings.full_screen_size.x + 1) / 2; + size.y = (p_settings.full_screen_size.y + 1) / 2; + } RD::get_singleton()->compute_list_dispatch_threads(p_compute_list, size.x, size.y, 1); } @@ -1266,8 +1283,7 @@ void SSEffects::generate_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORe } RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant)); - Size2i size(p_settings.full_screen_size.x >> (ssao_half_size ? 2 : 1), p_settings.full_screen_size.y >> (ssao_half_size ? 2 : 1)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, size.x, size.y, 1); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssao_buffers.buffer_width, p_ssao_buffers.buffer_height, 1); } if (ssao_quality > RS::ENV_SSAO_QUALITY_VERY_LOW) { diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index e1dc9f8624..609fb2afe7 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -151,11 +151,11 @@ void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_interna uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; if (msaa_3d == RS::VIEWPORT_MSAA_DISABLED) { - format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, (RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT)) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; usage_bits |= RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, usage_bits) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; } else { format = RD::DATA_FORMAT_R32_SFLOAT; - usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | (can_be_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : 0); } create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, format, usage_bits); diff --git a/thirdparty/README.md b/thirdparty/README.md index f883a3a6da..92e0a91596 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -70,6 +70,11 @@ Files extracted from upstream source: - all .cpp, .h, and .txt files except the folders MakeTables and etc2packer. +Changes related to BC6H packing and unpacking made upstream in +https://github.com/elasota/cvtt/commit/2e4b6b2747aec11f4cc6dd09ef43fa8ce769f6e2 +have been removed as they caused massive quality regressions. Apply the patches +in the `patches/` folder when syncing on newer upstream commits. + ## doctest diff --git a/thirdparty/cvtt/ConvectionKernels_BC67.cpp b/thirdparty/cvtt/ConvectionKernels_BC67.cpp index 021d658c08..011f51570b 100644 --- a/thirdparty/cvtt/ConvectionKernels_BC67.cpp +++ b/thirdparty/cvtt/ConvectionKernels_BC67.cpp @@ -76,6 +76,205 @@ namespace cvtt }; } + namespace BC6HData + { + enum EField + { + NA, // N/A + M, // Mode + D, // Shape + RW, + RX, + RY, + RZ, + GW, + GX, + GY, + GZ, + BW, + BX, + BY, + BZ, + }; + + struct ModeDescriptor + { + EField m_eField; + uint8_t m_uBit; + }; + + const ModeDescriptor g_modeDescriptors[14][82] = + { + { // Mode 1 (0x00) - 10 5 5 5 + { M, 0 },{ M, 1 },{ GY, 4 },{ BY, 4 },{ BZ, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, + { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 2 (0x01) - 7 6 6 6 + { M, 0 },{ M, 1 },{ GY, 5 },{ GZ, 4 },{ GZ, 5 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ BZ, 0 },{ BZ, 1 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ BY, 5 },{ BZ, 2 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BZ, 3 },{ BZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { RX, 5 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { GX, 5 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BX, 5 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, + { RY, 5 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ RZ, 5 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 3 (0x02) - 11 5 4 4 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { RW,10 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GW,10 }, + { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BW,10 }, + { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, + { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 4 (0x06) - 11 4 5 4 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RW,10 }, + { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { GW,10 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BW,10 }, + { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ BZ, 0 }, + { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ GY, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 5 (0x0a) - 11 4 4 5 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RW,10 }, + { BY, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GW,10 }, + { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BW,10 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ BZ, 1 }, + { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ BZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 6 (0x0e) - 9 5 5 5 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, + { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 7 (0x12) - 8 6 5 5 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ GZ, 4 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ BZ, 2 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BZ, 3 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { RX, 5 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, + { RY, 5 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ RZ, 5 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 8 (0x16) - 8 5 6 5 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ BZ, 0 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GY, 5 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ GZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { GX, 5 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, + { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 9 (0x1a) - 8 5 5 6 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ BZ, 1 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ BY, 5 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BX, 5 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, + { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 10 (0x1e) - 6 6 6 6 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ GZ, 4 },{ BZ, 0 },{ BZ, 1 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GY, 5 },{ BY, 5 },{ BZ, 2 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ GZ, 5 },{ BZ, 3 },{ BZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { RX, 5 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { GX, 5 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BX, 5 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, + { RY, 5 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ RZ, 5 },{ D, 0 },{ D, 1 },{ D, 2 }, + { D, 3 },{ D, 4 }, + }, + + { // Mode 11 (0x03) - 10 10 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { RX, 5 },{ RX, 6 },{ RX, 7 },{ RX, 8 },{ RX, 9 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { GX, 5 },{ GX, 6 },{ GX, 7 },{ GX, 8 },{ GX, 9 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BX, 5 },{ BX, 6 },{ BX, 7 },{ BX, 8 },{ BX, 9 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, + { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, + { NA, 0 },{ NA, 0 }, + }, + + { // Mode 12 (0x07) - 11 9 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { RX, 5 },{ RX, 6 },{ RX, 7 },{ RX, 8 },{ RW,10 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { GX, 5 },{ GX, 6 },{ GX, 7 },{ GX, 8 },{ GW,10 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BX, 5 },{ BX, 6 },{ BX, 7 },{ BX, 8 },{ BW,10 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, + { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, + { NA, 0 },{ NA, 0 }, + }, + + { // Mode 13 (0x0b) - 12 8 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, + { RX, 5 },{ RX, 6 },{ RX, 7 },{ RW,11 },{ RW,10 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, + { GX, 5 },{ GX, 6 },{ GX, 7 },{ GW,11 },{ GW,10 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, + { BX, 5 },{ BX, 6 },{ BX, 7 },{ BW,11 },{ BW,10 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, + { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, + { NA, 0 },{ NA, 0 }, + }, + + { // Mode 14 (0x0f) - 16 4 + { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, + { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, + { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, + { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RW,15 }, + { RW,14 },{ RW,13 },{ RW,12 },{ RW,11 },{ RW,10 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GW,15 }, + { GW,14 },{ GW,13 },{ GW,12 },{ GW,11 },{ GW,10 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BW,15 }, + { BW,14 },{ BW,13 },{ BW,12 },{ BW,11 },{ BW,10 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, + { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, + { NA, 0 },{ NA, 0 }, + }, + }; + } + namespace BC7Data { enum AlphaMode @@ -2998,9 +3197,9 @@ void cvtt::Internal::BC6HComputer::Pack(uint32_t flags, const PixelBlockF16* inp const BC7Data::BC6HModeInfo& modeInfo = BC7Data::g_hdrModes[mode]; - BC6H_IO::WriteFunc_t writeFunc = BC6H_IO::g_writeFuncs[mode]; + const BC6HData::ModeDescriptor *desc = BC6HData::g_modeDescriptors[mode]; - const int headerBits = modeInfo.m_partitioned ? 82 : 65; + const size_t headerBits = modeInfo.m_partitioned ? 82 : 65; for (int subset = 0; subset < 2; subset++) { @@ -3017,16 +3216,58 @@ void cvtt::Internal::BC6HComputer::Pack(uint32_t flags, const PixelBlockF16* inp uint16_t modeID = modeInfo.m_modeID; PackingVector pv; + pv.Init(); - { - uint32_t header[3]; - writeFunc(header, modeID, partition, - eps[0][0][0], eps[0][1][0], eps[1][0][0], eps[1][1][0], - eps[0][0][1], eps[0][1][1], eps[1][0][1], eps[1][1][1], - eps[0][0][2], eps[0][1][2], eps[1][0][2], eps[1][1][2] - ); - - pv.InitPacked(header, headerBits); + for (size_t i = 0; i < headerBits; i++) { + int32_t codedValue = 0; + switch (desc[i].m_eField) { + case BC6HData::M: + codedValue = modeID; + break; + case BC6HData::D: + codedValue = partition; + break; + case BC6HData::RW: + codedValue = eps[0][0][0]; + break; + case BC6HData::RX: + codedValue = eps[0][1][0]; + break; + case BC6HData::RY: + codedValue = eps[1][0][0]; + break; + case BC6HData::RZ: + codedValue = eps[1][1][0]; + break; + case BC6HData::GW: + codedValue = eps[0][0][1]; + break; + case BC6HData::GX: + codedValue = eps[0][1][1]; + break; + case BC6HData::GY: + codedValue = eps[1][0][1]; + break; + case BC6HData::GZ: + codedValue = eps[1][1][1]; + break; + case BC6HData::BW: + codedValue = eps[0][0][2]; + break; + case BC6HData::BX: + codedValue = eps[0][1][2]; + break; + case BC6HData::BY: + codedValue = eps[1][0][2]; + break; + case BC6HData::BZ: + codedValue = eps[1][1][2]; + break; + default: + assert(false); + break; + } + pv.Pack(static_cast<uint16_t>((codedValue >> desc[i].m_uBit) & 1), 1); } int fixupIndex1 = 0; @@ -3058,11 +3299,14 @@ void cvtt::Internal::BC6HComputer::SignExtendSingle(int &v, int bits) void cvtt::Internal::BC6HComputer::UnpackOne(PixelBlockF16 &output, const uint8_t *pBC, bool isSigned) { + UnpackingVector pv; + pv.Init(pBC); + int numModeBits = 2; - int modeBits = pBC[0] & 0x3; + int modeBits = pv.Unpack(2); if (modeBits != 0 && modeBits != 1) { - modeBits = pBC[0] & 0x1f; + modeBits |= pv.Unpack(3) << 2; numModeBits += 3; } @@ -3088,10 +3332,10 @@ void cvtt::Internal::BC6HComputer::UnpackOne(PixelBlockF16 &output, const uint8_ } const BC7Data::BC6HModeInfo& modeInfo = BC7Data::g_hdrModes[mode]; - const int headerBits = modeInfo.m_partitioned ? 82 : 65; - const BC6H_IO::ReadFunc_t readFunc = BC6H_IO::g_readFuncs[mode]; + const size_t headerBits = modeInfo.m_partitioned ? 82 : 65; + const BC6HData::ModeDescriptor *desc = BC6HData::g_modeDescriptors[mode]; - uint16_t partition = 0; + int32_t partition = 0; int32_t eps[2][2][3]; for (int subset = 0; subset < 2; subset++) @@ -3099,24 +3343,55 @@ void cvtt::Internal::BC6HComputer::UnpackOne(PixelBlockF16 &output, const uint8_ for (int ch = 0; ch < 3; ch++) eps[subset][epi][ch] = 0; - UnpackingVector pv; - pv.Init(pBC); - - { - uint32_t header[3]; - uint16_t codedEPs[2][2][3]; - pv.UnpackStart(header, headerBits); + for (size_t i = numModeBits; i < headerBits; i++) { + int32_t *pCodedValue = NULL; - readFunc(header, partition, - codedEPs[0][0][0], codedEPs[0][1][0], codedEPs[1][0][0], codedEPs[1][1][0], - codedEPs[0][0][1], codedEPs[0][1][1], codedEPs[1][0][1], codedEPs[1][1][1], - codedEPs[0][0][2], codedEPs[0][1][2], codedEPs[1][0][2], codedEPs[1][1][2] - ); + switch (desc[i].m_eField) { + case BC6HData::D: + pCodedValue = &partition; + break; + case BC6HData::RW: + pCodedValue = &eps[0][0][0]; + break; + case BC6HData::RX: + pCodedValue = &eps[0][1][0]; + break; + case BC6HData::RY: + pCodedValue = &eps[1][0][0]; + break; + case BC6HData::RZ: + pCodedValue = &eps[1][1][0]; + break; + case BC6HData::GW: + pCodedValue = &eps[0][0][1]; + break; + case BC6HData::GX: + pCodedValue = &eps[0][1][1]; + break; + case BC6HData::GY: + pCodedValue = &eps[1][0][1]; + break; + case BC6HData::GZ: + pCodedValue = &eps[1][1][1]; + break; + case BC6HData::BW: + pCodedValue = &eps[0][0][2]; + break; + case BC6HData::BX: + pCodedValue = &eps[0][1][2]; + break; + case BC6HData::BY: + pCodedValue = &eps[1][0][2]; + break; + case BC6HData::BZ: + pCodedValue = &eps[1][1][2]; + break; + default: + assert(false); + break; + } - for (int subset = 0; subset < 2; subset++) - for (int epi = 0; epi < 2; epi++) - for (int ch = 0; ch < 3; ch++) - eps[subset][epi][ch] = codedEPs[subset][epi][ch]; + (*pCodedValue) |= pv.Unpack(1) << desc[i].m_uBit; } uint16_t modeID = modeInfo.m_modeID; diff --git a/thirdparty/cvtt/patches/revert_BC6H_reorg.patch b/thirdparty/cvtt/patches/revert_BC6H_reorg.patch new file mode 100644 index 0000000000..11f9b9db46 --- /dev/null +++ b/thirdparty/cvtt/patches/revert_BC6H_reorg.patch @@ -0,0 +1,393 @@ +diff --git a/thirdparty/cvtt/ConvectionKernels_BC67.cpp b/thirdparty/cvtt/ConvectionKernels_BC67.cpp +index 021d658c08..011f51570b 100644 +--- a/thirdparty/cvtt/ConvectionKernels_BC67.cpp ++++ b/thirdparty/cvtt/ConvectionKernels_BC67.cpp +@@ -76,6 +76,205 @@ namespace cvtt + }; + } + ++ namespace BC6HData ++ { ++ enum EField ++ { ++ NA, // N/A ++ M, // Mode ++ D, // Shape ++ RW, ++ RX, ++ RY, ++ RZ, ++ GW, ++ GX, ++ GY, ++ GZ, ++ BW, ++ BX, ++ BY, ++ BZ, ++ }; ++ ++ struct ModeDescriptor ++ { ++ EField m_eField; ++ uint8_t m_uBit; ++ }; ++ ++ const ModeDescriptor g_modeDescriptors[14][82] = ++ { ++ { // Mode 1 (0x00) - 10 5 5 5 ++ { M, 0 },{ M, 1 },{ GY, 4 },{ BY, 4 },{ BZ, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, ++ { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 2 (0x01) - 7 6 6 6 ++ { M, 0 },{ M, 1 },{ GY, 5 },{ GZ, 4 },{ GZ, 5 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ BZ, 0 },{ BZ, 1 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ BY, 5 },{ BZ, 2 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BZ, 3 },{ BZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { RX, 5 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { GX, 5 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BX, 5 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, ++ { RY, 5 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ RZ, 5 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 3 (0x02) - 11 5 4 4 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { RW,10 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GW,10 }, ++ { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BW,10 }, ++ { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, ++ { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 4 (0x06) - 11 4 5 4 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RW,10 }, ++ { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { GW,10 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BW,10 }, ++ { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ BZ, 0 }, ++ { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ GY, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 5 (0x0a) - 11 4 4 5 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RW,10 }, ++ { BY, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GW,10 }, ++ { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BW,10 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ BZ, 1 }, ++ { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ BZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 6 (0x0e) - 9 5 5 5 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, ++ { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 7 (0x12) - 8 6 5 5 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ GZ, 4 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ BZ, 2 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BZ, 3 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { RX, 5 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, ++ { RY, 5 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ RZ, 5 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 8 (0x16) - 8 5 6 5 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ BZ, 0 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GY, 5 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ GZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { GX, 5 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, ++ { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 9 (0x1a) - 8 5 5 6 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ BZ, 1 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ BY, 5 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BX, 5 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, ++ { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 10 (0x1e) - 6 6 6 6 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ GZ, 4 },{ BZ, 0 },{ BZ, 1 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GY, 5 },{ BY, 5 },{ BZ, 2 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ GZ, 5 },{ BZ, 3 },{ BZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { RX, 5 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { GX, 5 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BX, 5 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 }, ++ { RY, 5 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ RZ, 5 },{ D, 0 },{ D, 1 },{ D, 2 }, ++ { D, 3 },{ D, 4 }, ++ }, ++ ++ { // Mode 11 (0x03) - 10 10 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { RX, 5 },{ RX, 6 },{ RX, 7 },{ RX, 8 },{ RX, 9 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { GX, 5 },{ GX, 6 },{ GX, 7 },{ GX, 8 },{ GX, 9 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BX, 5 },{ BX, 6 },{ BX, 7 },{ BX, 8 },{ BX, 9 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, ++ { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, ++ { NA, 0 },{ NA, 0 }, ++ }, ++ ++ { // Mode 12 (0x07) - 11 9 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { RX, 5 },{ RX, 6 },{ RX, 7 },{ RX, 8 },{ RW,10 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { GX, 5 },{ GX, 6 },{ GX, 7 },{ GX, 8 },{ GW,10 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BX, 5 },{ BX, 6 },{ BX, 7 },{ BX, 8 },{ BW,10 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, ++ { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, ++ { NA, 0 },{ NA, 0 }, ++ }, ++ ++ { // Mode 13 (0x0b) - 12 8 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 }, ++ { RX, 5 },{ RX, 6 },{ RX, 7 },{ RW,11 },{ RW,10 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 }, ++ { GX, 5 },{ GX, 6 },{ GX, 7 },{ GW,11 },{ GW,10 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 }, ++ { BX, 5 },{ BX, 6 },{ BX, 7 },{ BW,11 },{ BW,10 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, ++ { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, ++ { NA, 0 },{ NA, 0 }, ++ }, ++ ++ { // Mode 14 (0x0f) - 16 4 ++ { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 }, ++ { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 }, ++ { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 }, ++ { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RW,15 }, ++ { RW,14 },{ RW,13 },{ RW,12 },{ RW,11 },{ RW,10 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GW,15 }, ++ { GW,14 },{ GW,13 },{ GW,12 },{ GW,11 },{ GW,10 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BW,15 }, ++ { BW,14 },{ BW,13 },{ BW,12 },{ BW,11 },{ BW,10 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, ++ { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 }, ++ { NA, 0 },{ NA, 0 }, ++ }, ++ }; ++ } ++ + namespace BC7Data + { + enum AlphaMode +@@ -2998,9 +3197,9 @@ void cvtt::Internal::BC6HComputer::Pack(uint32_t flags, const PixelBlockF16* inp + + const BC7Data::BC6HModeInfo& modeInfo = BC7Data::g_hdrModes[mode]; + +- BC6H_IO::WriteFunc_t writeFunc = BC6H_IO::g_writeFuncs[mode]; ++ const BC6HData::ModeDescriptor *desc = BC6HData::g_modeDescriptors[mode]; + +- const int headerBits = modeInfo.m_partitioned ? 82 : 65; ++ const size_t headerBits = modeInfo.m_partitioned ? 82 : 65; + + for (int subset = 0; subset < 2; subset++) + { +@@ -3017,16 +3216,58 @@ void cvtt::Internal::BC6HComputer::Pack(uint32_t flags, const PixelBlockF16* inp + uint16_t modeID = modeInfo.m_modeID; + + PackingVector pv; ++ pv.Init(); + +- { +- uint32_t header[3]; +- writeFunc(header, modeID, partition, +- eps[0][0][0], eps[0][1][0], eps[1][0][0], eps[1][1][0], +- eps[0][0][1], eps[0][1][1], eps[1][0][1], eps[1][1][1], +- eps[0][0][2], eps[0][1][2], eps[1][0][2], eps[1][1][2] +- ); +- +- pv.InitPacked(header, headerBits); ++ for (size_t i = 0; i < headerBits; i++) { ++ int32_t codedValue = 0; ++ switch (desc[i].m_eField) { ++ case BC6HData::M: ++ codedValue = modeID; ++ break; ++ case BC6HData::D: ++ codedValue = partition; ++ break; ++ case BC6HData::RW: ++ codedValue = eps[0][0][0]; ++ break; ++ case BC6HData::RX: ++ codedValue = eps[0][1][0]; ++ break; ++ case BC6HData::RY: ++ codedValue = eps[1][0][0]; ++ break; ++ case BC6HData::RZ: ++ codedValue = eps[1][1][0]; ++ break; ++ case BC6HData::GW: ++ codedValue = eps[0][0][1]; ++ break; ++ case BC6HData::GX: ++ codedValue = eps[0][1][1]; ++ break; ++ case BC6HData::GY: ++ codedValue = eps[1][0][1]; ++ break; ++ case BC6HData::GZ: ++ codedValue = eps[1][1][1]; ++ break; ++ case BC6HData::BW: ++ codedValue = eps[0][0][2]; ++ break; ++ case BC6HData::BX: ++ codedValue = eps[0][1][2]; ++ break; ++ case BC6HData::BY: ++ codedValue = eps[1][0][2]; ++ break; ++ case BC6HData::BZ: ++ codedValue = eps[1][1][2]; ++ break; ++ default: ++ assert(false); ++ break; ++ } ++ pv.Pack(static_cast<uint16_t>((codedValue >> desc[i].m_uBit) & 1), 1); + } + + int fixupIndex1 = 0; +@@ -3058,11 +3299,14 @@ void cvtt::Internal::BC6HComputer::SignExtendSingle(int &v, int bits) + + void cvtt::Internal::BC6HComputer::UnpackOne(PixelBlockF16 &output, const uint8_t *pBC, bool isSigned) + { ++ UnpackingVector pv; ++ pv.Init(pBC); ++ + int numModeBits = 2; +- int modeBits = pBC[0] & 0x3; ++ int modeBits = pv.Unpack(2); + if (modeBits != 0 && modeBits != 1) + { +- modeBits = pBC[0] & 0x1f; ++ modeBits |= pv.Unpack(3) << 2; + numModeBits += 3; + } + +@@ -3088,10 +3332,10 @@ void cvtt::Internal::BC6HComputer::UnpackOne(PixelBlockF16 &output, const uint8_ + } + + const BC7Data::BC6HModeInfo& modeInfo = BC7Data::g_hdrModes[mode]; +- const int headerBits = modeInfo.m_partitioned ? 82 : 65; +- const BC6H_IO::ReadFunc_t readFunc = BC6H_IO::g_readFuncs[mode]; ++ const size_t headerBits = modeInfo.m_partitioned ? 82 : 65; ++ const BC6HData::ModeDescriptor *desc = BC6HData::g_modeDescriptors[mode]; + +- uint16_t partition = 0; ++ int32_t partition = 0; + int32_t eps[2][2][3]; + + for (int subset = 0; subset < 2; subset++) +@@ -3099,24 +3343,55 @@ void cvtt::Internal::BC6HComputer::UnpackOne(PixelBlockF16 &output, const uint8_ + for (int ch = 0; ch < 3; ch++) + eps[subset][epi][ch] = 0; + +- UnpackingVector pv; +- pv.Init(pBC); +- +- { +- uint32_t header[3]; +- uint16_t codedEPs[2][2][3]; +- pv.UnpackStart(header, headerBits); ++ for (size_t i = numModeBits; i < headerBits; i++) { ++ int32_t *pCodedValue = NULL; + +- readFunc(header, partition, +- codedEPs[0][0][0], codedEPs[0][1][0], codedEPs[1][0][0], codedEPs[1][1][0], +- codedEPs[0][0][1], codedEPs[0][1][1], codedEPs[1][0][1], codedEPs[1][1][1], +- codedEPs[0][0][2], codedEPs[0][1][2], codedEPs[1][0][2], codedEPs[1][1][2] +- ); ++ switch (desc[i].m_eField) { ++ case BC6HData::D: ++ pCodedValue = &partition; ++ break; ++ case BC6HData::RW: ++ pCodedValue = &eps[0][0][0]; ++ break; ++ case BC6HData::RX: ++ pCodedValue = &eps[0][1][0]; ++ break; ++ case BC6HData::RY: ++ pCodedValue = &eps[1][0][0]; ++ break; ++ case BC6HData::RZ: ++ pCodedValue = &eps[1][1][0]; ++ break; ++ case BC6HData::GW: ++ pCodedValue = &eps[0][0][1]; ++ break; ++ case BC6HData::GX: ++ pCodedValue = &eps[0][1][1]; ++ break; ++ case BC6HData::GY: ++ pCodedValue = &eps[1][0][1]; ++ break; ++ case BC6HData::GZ: ++ pCodedValue = &eps[1][1][1]; ++ break; ++ case BC6HData::BW: ++ pCodedValue = &eps[0][0][2]; ++ break; ++ case BC6HData::BX: ++ pCodedValue = &eps[0][1][2]; ++ break; ++ case BC6HData::BY: ++ pCodedValue = &eps[1][0][2]; ++ break; ++ case BC6HData::BZ: ++ pCodedValue = &eps[1][1][2]; ++ break; ++ default: ++ assert(false); ++ break; ++ } + +- for (int subset = 0; subset < 2; subset++) +- for (int epi = 0; epi < 2; epi++) +- for (int ch = 0; ch < 3; ch++) +- eps[subset][epi][ch] = codedEPs[subset][epi][ch]; ++ (*pCodedValue) |= pv.Unpack(1) << desc[i].m_uBit; + } + + uint16_t modeID = modeInfo.m_modeID; |