diff options
311 files changed, 24211 insertions, 1557 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index c812996fd4..01f2404866 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -130,7 +130,7 @@ jobs: - name: Generate C# glue if: ${{ matrix.build-mono }} run: | - ${{ matrix.bin }} --headless --generate-mono-glue ./modules/mono/glue || true + ${{ matrix.bin }} --headless --generate-mono-glue ./modules/mono/glue - name: Build .NET solutions if: ${{ matrix.build-mono }} diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index f884c690fa..aa98d69ca4 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -256,6 +256,12 @@ Comment: jpeg-compressor Copyright: 2012, Rich Geldreich License: public-domain or Apache-2.0 +Files: ./thirdparty/libktx/ +Comment: KTX +Copyright: 2013-2020, Mark Callow + 2010-2020 The Khronos Group, Inc. +License: Apache-2.0 + Files: ./thirdparty/libogg/ Comment: OggVorbis Copyright: 2002, Xiph.org Foundation diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 2332bc235b..33b3271495 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -420,6 +420,10 @@ void register_global_constants() { BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHD); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHE); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHF); + BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GLOBE); + BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KEYBOARD); + BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_EISU); + BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_KANA); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UNKNOWN); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPACE); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EXCLAM); @@ -492,10 +496,6 @@ void register_global_constants() { BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ASCIITILDE); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, YEN); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SECTION); - BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GLOBE); - BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KEYBOARD); - BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_EISU); - BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_KANA); BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_CODE_MASK, CODE_MASK); BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_MODIFIER_MASK, MODIFIER_MASK); diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index e37886cbe9..9e341b2157 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -1722,7 +1722,27 @@ String InputEventMIDI::as_text() const { } String InputEventMIDI::to_string() { - return vformat("InputEventMIDI: channel=%d, message=%d, pitch=%d, velocity=%d, pressure=%d, controller_number=%d, controller_value=%d", channel, message, pitch, velocity, pressure, controller_number, controller_value); + String ret; + switch (message) { + case MIDIMessage::NOTE_ON: + ret = vformat("Note On: channel=%d, pitch=%d, velocity=%d", channel, pitch, velocity); + break; + case MIDIMessage::NOTE_OFF: + ret = vformat("Note Off: channel=%d, pitch=%d, velocity=%d", channel, pitch, velocity); + break; + case MIDIMessage::PITCH_BEND: + ret = vformat("Pitch Bend: channel=%d, pitch=%d", channel, pitch); + break; + case MIDIMessage::CHANNEL_PRESSURE: + ret = vformat("Channel Pressure: channel=%d, pressure=%d", channel, pressure); + break; + case MIDIMessage::CONTROL_CHANGE: + ret = vformat("Control Change: channel=%d, controller_number=%d, controller_value=%d", channel, controller_number, controller_value); + break; + default: + ret = vformat("channel=%d, message=%d, pitch=%d, velocity=%d, pressure=%d, controller_number=%d, controller_value=%d, instrument=%d", channel, message, pitch, velocity, pressure, controller_number, controller_value, instrument); + } + return "InputEventMIDI: " + ret; } void InputEventMIDI::_bind_methods() { diff --git a/core/io/image.cpp b/core/io/image.cpp index a5fea09113..3ca39f98c0 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3018,6 +3018,7 @@ ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr; ImageMemLoadFunc Image::_dds_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_ktx_mem_loader_func = nullptr; void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr; @@ -3490,6 +3491,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer); ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer); ClassDB::bind_method(D_METHOD("load_dds_from_buffer", "buffer"), &Image::load_dds_from_buffer); + ClassDB::bind_method(D_METHOD("load_ktx_from_buffer", "buffer"), &Image::load_ktx_from_buffer); ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0)); @@ -3873,6 +3875,14 @@ Error Image::load_dds_from_buffer(const Vector<uint8_t> &p_array) { return _load_from_buffer(p_array, _dds_mem_loader_func); } +Error Image::load_ktx_from_buffer(const Vector<uint8_t> &p_array) { + ERR_FAIL_NULL_V_MSG( + _ktx_mem_loader_func, + ERR_UNAVAILABLE, + "The KTX module isn't enabled. Recompile the Godot editor or export template binary with the `module_ktx_enabled=yes` SCons option."); + return _load_from_buffer(p_array, _ktx_mem_loader_func); +} + void Image::convert_rg_to_ra_rgba8() { ERR_FAIL_COND(format != FORMAT_RGBA8); ERR_FAIL_COND(!data.size()); diff --git a/core/io/image.h b/core/io/image.h index f68543ba24..cb7c6bff52 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -151,6 +151,7 @@ public: static ImageMemLoadFunc _bmp_mem_loader_func; static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func; static ImageMemLoadFunc _dds_mem_loader_func; + static ImageMemLoadFunc _ktx_mem_loader_func; static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels); @@ -404,6 +405,7 @@ public: Error load_tga_from_buffer(const Vector<uint8_t> &p_array); Error load_bmp_from_buffer(const Vector<uint8_t> &p_array); Error load_dds_from_buffer(const Vector<uint8_t> &p_array); + Error load_ktx_from_buffer(const Vector<uint8_t> &p_array); Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0); Error load_svg_from_string(const String &p_svg_str, float scale = 1.0); diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index c8c50fb957..e9fd8ad583 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -1661,7 +1661,15 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { c.name = p_extension->class_name; c.is_virtual = p_extension->is_virtual; if (!p_extension->is_abstract) { - c.creation_func = parent->creation_func; + // Find the closest ancestor which is either non-abstract or native (or both). + ClassInfo *concrete_ancestor = parent; + while (concrete_ancestor->creation_func == nullptr && + concrete_ancestor->inherits_ptr != nullptr && + concrete_ancestor->gdextension != nullptr) { + concrete_ancestor = concrete_ancestor->inherits_ptr; + } + ERR_FAIL_NULL_MSG(concrete_ancestor->creation_func, "Extension class " + String(p_extension->class_name) + " cannot extend native abstract class " + String(concrete_ancestor->name)); + c.creation_func = concrete_ancestor->creation_func; } c.inherits = parent->name; c.class_ptr = parent->class_ptr; diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index 1a51624030..6078882839 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -146,6 +146,7 @@ static const _KeyCodeText _keycodes[] = { {Key::FAVORITES ,"Favorites"}, {Key::SEARCH ,"Search"}, {Key::STANDBY ,"StandBy"}, + {Key::OPENURL ,"OpenURL"}, {Key::LAUNCHMAIL ,"LaunchMail"}, {Key::LAUNCHMEDIA ,"LaunchMedia"}, {Key::LAUNCH0 ,"Launch0"}, @@ -238,6 +239,8 @@ static const _KeyCodeText _keycodes[] = { {Key::BAR ,"Bar"}, {Key::BRACERIGHT ,"BraceRight"}, {Key::ASCIITILDE ,"AsciiTilde"}, + {Key::YEN ,"Yen"}, + {Key::SECTION ,"Section"}, {Key::NONE ,nullptr} /* clang-format on */ }; diff --git a/core/os/keyboard.h b/core/os/keyboard.h index cf276dc49f..785972d31d 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -33,6 +33,8 @@ #include "core/string/ustring.h" +// Keep the values in this enum in sync with `_keycodes` in `keyboard.cpp`, +// and the bindings in `core_constants.cpp`. enum class Key { NONE = 0, // Special key: The strategy here is similar to the one used by toolkits, diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 80ca51573c..3f11459a1e 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -3665,7 +3665,9 @@ String String::left(int p_len) const { return *this; } - return substr(0, p_len); + String s; + s.copy_from_unchecked(&get_data()[0], p_len); + return s; } String String::right(int p_len) const { @@ -3681,7 +3683,9 @@ String String::right(int p_len) const { return *this; } - return substr(length() - p_len); + String s; + s.copy_from_unchecked(&get_data()[length() - p_len], p_len); + return s; } char32_t String::unicode_at(int p_idx) const { diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h index 98afc7e717..055c52aa63 100644 --- a/core/variant/typed_array.h +++ b/core/variant/typed_array.h @@ -230,4 +230,7 @@ MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY) MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY) MAKE_TYPED_ARRAY_INFO(IPAddress, Variant::STRING) +#undef MAKE_TYPED_ARRAY +#undef MAKE_TYPED_ARRAY_INFO + #endif // TYPED_ARRAY_H diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 10a267e5a9..8a0289898d 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -1754,11 +1754,10 @@ String Variant::stringify(int recursion_count) const { case COLOR: return operator Color(); case DICTIONARY: { + ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "{ ... }", "Maximum dictionary recursion reached!"); + recursion_count++; + const Dictionary &d = *reinterpret_cast<const Dictionary *>(_data._mem); - if (recursion_count > MAX_RECURSION) { - ERR_PRINT("Maximum dictionary recursion reached!"); - return "{ ... }"; - } // Add leading and trailing space to Dictionary printing. This distinguishes it // from array printing on fonts that have similar-looking {} and [] characters. @@ -1768,7 +1767,6 @@ String Variant::stringify(int recursion_count) const { Vector<_VariantStrPair> pairs; - recursion_count++; for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { _VariantStrPair sp; sp.key = stringify_variant_clean(E->get(), recursion_count); @@ -1787,6 +1785,7 @@ String Variant::stringify(int recursion_count) const { return str; } + // Packed arrays cannot contain recursive structures, the recursion_count increment is not needed. case PACKED_VECTOR2_ARRAY: { return stringify_vector(operator Vector<Vector2>(), recursion_count); } @@ -1815,13 +1814,10 @@ String Variant::stringify(int recursion_count) const { return stringify_vector(operator Vector<double>(), recursion_count); } case ARRAY: { - Array arr = operator Array(); - if (recursion_count > MAX_RECURSION) { - ERR_PRINT("Maximum array recursion reached!"); - return "[...]"; - } + ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "[...]", "Maximum array recursion reached!"); + recursion_count++; - return stringify_vector(arr, recursion_count); + return stringify_vector(operator Array(), recursion_count); } case OBJECT: { if (_get_obj().obj) { diff --git a/doc/class.xsd b/doc/class.xsd index 22821ac767..587f3f3c97 100644 --- a/doc/class.xsd +++ b/doc/class.xsd @@ -149,6 +149,8 @@ <xs:element type="xs:string" name="description" /> </xs:sequence> <xs:attribute type="xs:string" name="name" use="optional" /> + <xs:attribute type="xs:boolean" name="is_deprecated" use="optional" /> + <xs:attribute type="xs:boolean" name="is_experimental" use="optional" /> </xs:complexType> </xs:element> </xs:sequence> diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index e44fdd9776..ae40051d95 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2035,6 +2035,18 @@ <constant name="KEY_LAUNCHF" value="4194415" enum="Key"> Launch Shortcut F key. </constant> + <constant name="KEY_GLOBE" value="4194416" enum="Key"> + "Globe" key on Mac / iPad keyboard. + </constant> + <constant name="KEY_KEYBOARD" value="4194417" enum="Key"> + "On-screen keyboard" key on iPad keyboard. + </constant> + <constant name="KEY_JIS_EISU" value="4194418" enum="Key"> + 英数 key on Mac keyboard. + </constant> + <constant name="KEY_JIS_KANA" value="4194419" enum="Key"> + かな key on Mac keyboard. + </constant> <constant name="KEY_UNKNOWN" value="8388607" enum="Key"> Unknown key. </constant> @@ -2251,18 +2263,6 @@ <constant name="KEY_SECTION" value="167" enum="Key"> § key. </constant> - <constant name="KEY_GLOBE" value="4194416" enum="Key"> - "Globe" key on Mac / iPad keyboard. - </constant> - <constant name="KEY_KEYBOARD" value="4194417" enum="Key"> - "On-screen keyboard" key iPad keyboard. - </constant> - <constant name="KEY_JIS_EISU" value="4194418" enum="Key"> - 英数 key on Mac keyboard. - </constant> - <constant name="KEY_JIS_KANA" value="4194419" enum="Key"> - かな key on Mac keyboard. - </constant> <constant name="KEY_CODE_MASK" value="8388607" enum="KeyModifierMask" is_bitfield="true"> Key Code mask. </constant> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 999cc6fa29..fccf16fcaa 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -257,7 +257,7 @@ <param index="0" name="value" type="Variant" /> <description> Removes the first occurrence of a value from the array. If the value does not exist in the array, nothing happens. To remove an element by index, use [method remove_at] instead. - [b]Note:[/b] This method acts in-place and doesn't return a value. + [b]Note:[/b] This method acts in-place and doesn't return a modified array. [b]Note:[/b] On large arrays, this method will be slower if the removed element is close to the beginning of the array (index 0). This is because all elements placed after the removed element have to be reindexed. [b]Note:[/b] Do not erase entries while iterating over the array. </description> @@ -383,8 +383,8 @@ <param index="0" name="position" type="int" /> <param index="1" name="value" type="Variant" /> <description> - Inserts a new element at a given position in the array. The position must be valid, or at the end of the array ([code]pos == size()[/code]). - [b]Note:[/b] This method acts in-place and doesn't return a value. + Inserts a new element at a given position in the array. The position must be valid, or at the end of the array ([code]pos == size()[/code]). Returns [constant OK] on success, or one of the other [enum Error] values if the operation failed. + [b]Note:[/b] This method acts in-place and doesn't return a modified array. [b]Note:[/b] On large arrays, this method will be slower if the inserted element is close to the beginning of the array (index 0). This is because all elements placed after the newly inserted element have to be reindexed. </description> </method> @@ -462,7 +462,7 @@ <method name="pick_random" qualifiers="const"> <return type="Variant" /> <description> - Returns a random value from the target array. + Returns a random value from the target array. Prints an error and returns [code]null[/code] if the array is empty. [codeblocks] [gdscript] var array: Array[int] = [1, 2, 3, 4] @@ -534,7 +534,7 @@ <param index="0" name="position" type="int" /> <description> Removes an element from the array by index. If the index does not exist in the array, nothing happens. To remove an element by searching for its value, use [method erase] instead. - [b]Note:[/b] This method acts in-place and doesn't return a value. + [b]Note:[/b] This method acts in-place and doesn't return a modified array. [b]Note:[/b] On large arrays, this method will be slower if the removed element is close to the beginning of the array (index 0). This is because all elements placed after the removed element have to be reindexed. [b]Note:[/b] [param position] cannot be negative. To remove an element relative to the end of the array, use [code]arr.remove_at(arr.size() - (i + 1))[/code]. To remove the last element from the array without returning the value, use [code]arr.resize(arr.size() - 1)[/code]. </description> @@ -543,7 +543,8 @@ <return type="int" /> <param index="0" name="size" type="int" /> <description> - Resizes the array to contain a different number of elements. If the array size is smaller, elements are cleared, if bigger, new elements are [code]null[/code]. + Resizes the array to contain a different number of elements. If the array size is smaller, elements are cleared, if bigger, new elements are [code]null[/code]. Returns [constant OK] on success, or one of the other [enum Error] values if the operation failed. + [b]Note:[/b] This method acts in-place and doesn't return a modified array. </description> </method> <method name="reverse"> diff --git a/doc/classes/ArrayOccluder3D.xml b/doc/classes/ArrayOccluder3D.xml index 0e73d01847..7c7b440c76 100644 --- a/doc/classes/ArrayOccluder3D.xml +++ b/doc/classes/ArrayOccluder3D.xml @@ -8,6 +8,7 @@ See [OccluderInstance3D]'s documentation for instructions on setting up occlusion culling. </description> <tutorials> + <link title="Occlusion culling">$DOCS_URL/tutorials/3d/occlusion_culling.html</link> </tutorials> <methods> <method name="set_arrays"> diff --git a/doc/classes/AudioStreamMicrophone.xml b/doc/classes/AudioStreamMicrophone.xml index 189f9b95e2..2225ad2d35 100644 --- a/doc/classes/AudioStreamMicrophone.xml +++ b/doc/classes/AudioStreamMicrophone.xml @@ -8,6 +8,7 @@ [b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings. </description> <tutorials> + <link title="Recording with microphone">$DOCS_URL/tutorials/audio/recording_with_microphone.html</link> <link title="Audio Mic Record Demo">https://github.com/godotengine/godot-demo-projects/tree/master/audio/mic_record</link> </tutorials> </class> diff --git a/doc/classes/BoxOccluder3D.xml b/doc/classes/BoxOccluder3D.xml index 094a696936..ac9770fde1 100644 --- a/doc/classes/BoxOccluder3D.xml +++ b/doc/classes/BoxOccluder3D.xml @@ -8,6 +8,7 @@ See [OccluderInstance3D]'s documentation for instructions on setting up occlusion culling. </description> <tutorials> + <link title="Occlusion culling">$DOCS_URL/tutorials/3d/occlusion_culling.html</link> </tutorials> <members> <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(1, 1, 1)"> diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index bd096dc9c6..9d16f3e8a4 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -8,6 +8,7 @@ See also [GPUParticles3D], which provides the same functionality with hardware acceleration, but may not run on older devices. </description> <tutorials> + <link title="Particle systems (3D)">$DOCS_URL/tutorials/3d/particles/index.html</link> </tutorials> <methods> <method name="convert_from_particles"> diff --git a/doc/classes/CameraAttributesPhysical.xml b/doc/classes/CameraAttributesPhysical.xml index c11a057b1d..69af64b7ff 100644 --- a/doc/classes/CameraAttributesPhysical.xml +++ b/doc/classes/CameraAttributesPhysical.xml @@ -10,6 +10,7 @@ [b]Note:[/b] Depth of field blur is only supported in the Forward+ and Mobile rendering methods, not Compatibility. </description> <tutorials> + <link title="Physical light and camera units">$DOCS_URL/tutorials/3d/physical_light_and_camera_units.html</link> </tutorials> <methods> <method name="get_fov" qualifiers="const"> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index 292207e8bb..86e421b5b8 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -259,7 +259,7 @@ Color.html_is_valid("55AAFF") # Returns true Color.html_is_valid("#F2C") # Returns true - Color.html_is_valid("#AABBC) # Returns false + Color.html_is_valid("#AABBC") # Returns false Color.html_is_valid("#55aaFF5") # Returns false [/gdscript] [csharp] diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index ee790b6968..a870aa129a 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -19,6 +19,7 @@ <link title="GUI documentation index">$DOCS_URL/tutorials/ui/index.html</link> <link title="Custom drawing in 2D">$DOCS_URL/tutorials/2d/custom_drawing_in_2d.html</link> <link title="Control node gallery">$DOCS_URL/tutorials/ui/control_node_gallery.html</link> + <link title="Multiple resolutions">$DOCS_URL/tutorials/rendering/multiple_resolutions.html</link> <link title="All GUI Demos">https://github.com/godotengine/godot-demo-projects/tree/master/gui</link> </tutorials> <methods> diff --git a/doc/classes/CryptoKey.xml b/doc/classes/CryptoKey.xml index 32d5d361ba..ff826a3ae5 100644 --- a/doc/classes/CryptoKey.xml +++ b/doc/classes/CryptoKey.xml @@ -8,6 +8,7 @@ They can be used to generate a self-signed [X509Certificate] via [method Crypto.generate_self_signed_certificate] and as private key in [method StreamPeerTLS.accept_stream] along with the appropriate certificate. </description> <tutorials> + <link title="SSL certificates">$DOCS_URL/tutorials/networking/ssl_certificates.html</link> </tutorials> <methods> <method name="is_public_only" qualifiers="const"> diff --git a/doc/classes/DirectionalLight3D.xml b/doc/classes/DirectionalLight3D.xml index 1dbacd5ef7..d3cca5fa6d 100644 --- a/doc/classes/DirectionalLight3D.xml +++ b/doc/classes/DirectionalLight3D.xml @@ -7,7 +7,8 @@ A directional light is a type of [Light3D] node that models an infinite number of parallel rays covering the entire scene. It is used for lights with strong intensity that are located far away from the scene to model sunlight or moonlight. The worldspace location of the DirectionalLight3D transform (origin) is ignored. Only the basis is used to determine light direction. </description> <tutorials> - <link title="Lights and shadows">$DOCS_URL/tutorials/3d/lights_and_shadows.html</link> + <link title="3D lights and shadows">$DOCS_URL/tutorials/3d/lights_and_shadows.html</link> + <link title="Faking global illumination">$DOCS_URL/tutorials/3d/global_illumination/faking_global_illumination.html</link> </tutorials> <members> <member name="directional_shadow_blend_splits" type="bool" setter="set_blend_splits" getter="is_blend_splits_enabled" default="false"> diff --git a/doc/classes/EditorExportPlatform.xml b/doc/classes/EditorExportPlatform.xml index 48f3d46398..ac921a0c80 100644 --- a/doc/classes/EditorExportPlatform.xml +++ b/doc/classes/EditorExportPlatform.xml @@ -8,5 +8,6 @@ Used in scripting by [EditorExportPlugin] to configure platform-specific customization of scenes and resources. See [method EditorExportPlugin._begin_customize_scenes] and [method EditorExportPlugin._begin_customize_resources] for more details. </description> <tutorials> + <link title="$DOCS_URL/tutorials/platform/consoles.html">Console support in Godot</link> </tutorials> </class> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 8d280b8276..08f8cf70e9 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -749,9 +749,10 @@ Emitted when user changes the workspace ([b]2D[/b], [b]3D[/b], [b]Script[/b], [b]AssetLib[/b]). Also works with custom screens defined by plugins. </description> </signal> - <signal name="project_settings_changed"> + <signal name="project_settings_changed" is_deprecated="true"> <description> Emitted when any project setting has changed. + [i]Deprecated.[/i] Use [signal ProjectSettings.settings_changed] instead. </description> </signal> <signal name="resource_saved"> diff --git a/doc/classes/EditorResourcePreview.xml b/doc/classes/EditorResourcePreview.xml index d8c5855932..ed3fdae352 100644 --- a/doc/classes/EditorResourcePreview.xml +++ b/doc/classes/EditorResourcePreview.xml @@ -4,7 +4,7 @@ A node used to generate previews of resources or files. </brief_description> <description> - This node is used to generate previews for resources of files. + This node is used to generate previews for resources or files. [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorInterface.get_resource_previewer]. </description> <tutorials> diff --git a/doc/classes/EditorResourcePreviewGenerator.xml b/doc/classes/EditorResourcePreviewGenerator.xml index e504bf7980..fcfdbb5c44 100644 --- a/doc/classes/EditorResourcePreviewGenerator.xml +++ b/doc/classes/EditorResourcePreviewGenerator.xml @@ -25,7 +25,7 @@ Generate a preview from a given resource with the specified size. This must always be implemented. Returning an empty texture is an OK way to fail and let another generator take care. Care must be taken because this function is always called from a thread (not the main thread). - [param metadata] dictionary can modified to store file-specific metadata that can be used in [method EditorResourceTooltipPlugin._make_tooltip_for_path] (like image size, sample length etc.). + [param metadata] dictionary can be modified to store file-specific metadata that can be used in [method EditorResourceTooltipPlugin._make_tooltip_for_path] (like image size, sample length etc.). </description> </method> <method name="_generate_from_path" qualifiers="virtual const"> @@ -37,7 +37,7 @@ Generate a preview directly from a path with the specified size. Implementing this is optional, as default code will load and call [method _generate]. Returning an empty texture is an OK way to fail and let another generator take care. Care must be taken because this function is always called from a thread (not the main thread). - [param metadata] dictionary can modified to store file-specific metadata that can be used in [method EditorResourceTooltipPlugin._make_tooltip_for_path] (like image size, sample length etc.). + [param metadata] dictionary can be modified to store file-specific metadata that can be used in [method EditorResourceTooltipPlugin._make_tooltip_for_path] (like image size, sample length etc.). </description> </method> <method name="_generate_small_preview_automatically" qualifiers="virtual const"> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 5ca89dc03e..83abc963bb 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -295,7 +295,7 @@ The grid division bias to use in the 3D editor. Negative values will cause small grid divisions to appear earlier, whereas positive values will cause small grid divisions to appear later. </member> <member name="editors/3d/grid_division_level_max" type="int" setter="" getter=""> - The largest grid division to use in the 3D editor. Together with [member editors/3d/primary_grid_steps], this determines how large the grid divisions can be. The grid divisions will not be able to get larger than [code]primary_grid_steps ^ grid_division_level_max[/code] units. By default, when [member editors/3d/primary_grid_steps] is [code]8[/code], this means grid divisions cannot get larger than [code]64[/code] uints each (so primary grid lines are [code]512[/code] uints apart), no matter how far away the camera is from the grid. + The largest grid division to use in the 3D editor. Together with [member editors/3d/primary_grid_steps], this determines how large the grid divisions can be. The grid divisions will not be able to get larger than [code]primary_grid_steps ^ grid_division_level_max[/code] units. By default, when [member editors/3d/primary_grid_steps] is [code]8[/code], this means grid divisions cannot get larger than [code]64[/code] units each (so primary grid lines are [code]512[/code] units apart), no matter how far away the camera is from the grid. </member> <member name="editors/3d/grid_division_level_min" type="int" setter="" getter=""> The smallest grid division to use in the 3D editor. Together with [member editors/3d/primary_grid_steps], this determines how small the grid divisions can be. The grid divisions will not be able to get smaller than [code]primary_grid_steps ^ grid_division_level_min[/code] units. By default, this means grid divisions cannot get smaller than 1 unit each, no matter how close the camera is from the grid. @@ -477,6 +477,22 @@ <member name="filesystem/file_dialog/thumbnail_size" type="int" setter="" getter=""> The thumbnail size to use in the editor's file dialogs (in pixels). See also [member docks/filesystem/thumbnail_size]. </member> + <member name="filesystem/import/blender/blender3_path" type="String" setter="" getter=""> + The path to the directory containing the Blender executable used for converting the Blender 3D scene files [code].blend[/code] to glTF 2.0 format during import. Blender 3.0 or later is required. + To enable this feature for your specific project, use [member ProjectSettings.filesystem/import/blender/enabled]. + </member> + <member name="filesystem/import/blender/rpc_port" type="int" setter="" getter=""> + The port number used for Remote Procedure Call (RPC) communication with Godot's created process of the blender executable. + Setting this to 0 effectively disables communication with Godot and the blender process, making performance slower. + </member> + <member name="filesystem/import/blender/rpc_server_uptime" type="float" setter="" getter=""> + The maximum idle uptime (in seconds) of the Blender process. + This prevents Godot from having to create a new process for each import within the given seconds. + </member> + <member name="filesystem/import/fbx/fbx2gltf_path" type="String" setter="" getter=""> + The path to the FBX2glTF executable used for converting Autodesk FBX 3D scene files [code].fbx[/code] to glTF 2.0 format during import. + To enable this feature for your specific project, use [member ProjectSettings.filesystem/import/fbx/enabled]. + </member> <member name="filesystem/on_save/compress_binary_resources" type="bool" setter="" getter=""> If [code]true[/code], uses lossless compression for binary resources. </member> diff --git a/doc/classes/EditorSyntaxHighlighter.xml b/doc/classes/EditorSyntaxHighlighter.xml index 3bc845352f..3685907574 100644 --- a/doc/classes/EditorSyntaxHighlighter.xml +++ b/doc/classes/EditorSyntaxHighlighter.xml @@ -5,7 +5,7 @@ </brief_description> <description> Base class that all [SyntaxHighlighter]s used by the [ScriptEditor] extend from. - Add a syntax highlighter to an individual script by calling [method ScriptEditorBase.add_syntax_highlighter]. To apply to all scripts on open, call [method ScriptEditor.register_syntax_highlighter] + Add a syntax highlighter to an individual script by calling [method ScriptEditorBase.add_syntax_highlighter]. To apply to all scripts on open, call [method ScriptEditor.register_syntax_highlighter]. </description> <tutorials> </tutorials> diff --git a/doc/classes/EditorVCSInterface.xml b/doc/classes/EditorVCSInterface.xml index baf69f2c6d..03be5c332d 100644 --- a/doc/classes/EditorVCSInterface.xml +++ b/doc/classes/EditorVCSInterface.xml @@ -7,6 +7,7 @@ Defines the API that the editor uses to extract information from the underlying VCS. The implementation of this API is included in VCS plugins, which are GDExtension plugins that inherit [EditorVCSInterface] and are attached (on demand) to the singleton instance of [EditorVCSInterface]. Instead of performing the task themselves, all the virtual functions listed below are calling the internally overridden functions in the VCS plugins to provide a plug-n-play experience. A custom VCS plugin is supposed to inherit from [EditorVCSInterface] and override each of these virtual functions. </description> <tutorials> + <link title="Version control systems">$DOCS_URL/tutorials/best_practices/version_control_systems.html</link> </tutorials> <methods> <method name="_checkout_branch" qualifiers="virtual"> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index e2f76480a0..3b6263b67b 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -12,7 +12,7 @@ </description> <tutorials> <link title="Environment and post-processing">$DOCS_URL/tutorials/3d/environment_and_post_processing.html</link> - <link title="Light transport in game engines">$DOCS_URL/tutorials/3d/high_dynamic_range.html</link> + <link title="High dynamic range lighting">$DOCS_URL/tutorials/3d/high_dynamic_range.html</link> <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link> <link title="2D HDR Demo">https://godotengine.org/asset-library/asset/110</link> <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index 85fbff7932..e50e610e71 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -202,7 +202,7 @@ <return type="int" /> <param index="0" name="file" type="String" /> <description> - Returns the last time the [param file] was modified in Unix timestamp format or returns a [String] "ERROR IN [param file]". This Unix timestamp can be converted to another format using the [Time] singleton. + Returns the last time the [param file] was modified in Unix timestamp format, or [code]0[/code] on error. This Unix timestamp can be converted to another format using the [Time] singleton. </description> </method> <method name="get_open_error" qualifiers="static"> diff --git a/doc/classes/FogVolume.xml b/doc/classes/FogVolume.xml index 538411016e..74ccf569e7 100644 --- a/doc/classes/FogVolume.xml +++ b/doc/classes/FogVolume.xml @@ -9,6 +9,7 @@ [b]Note:[/b] [FogVolume]s only have a visible effect if [member Environment.volumetric_fog_enabled] is [code]true[/code]. If you don't want fog to be globally visible (but only within [FogVolume] nodes), set [member Environment.volumetric_fog_density] to [code]0.0[/code]. </description> <tutorials> + <link title="Volumetric fog and fog volumes">$DOCS_URL/tutorials/3d/volumetric_fog.html</link> </tutorials> <members> <member name="material" type="Material" setter="set_material" getter="get_material"> diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml index d5a4c146e0..ee55288783 100644 --- a/doc/classes/GPUParticles2D.xml +++ b/doc/classes/GPUParticles2D.xml @@ -21,6 +21,13 @@ [b]Note:[/b] When using threaded rendering this method synchronizes the rendering thread. Calling it often may have a negative impact on performance. </description> </method> + <method name="convert_from_particles"> + <return type="void" /> + <param index="0" name="particles" type="Node" /> + <description> + Sets this node's properties to match a given [CPUParticles2D] node. + </description> + </method> <method name="emit_particle"> <return type="void" /> <param index="0" name="xform" type="Transform2D" /> diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index 31f1f9e66e..9ba64feebc 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -8,6 +8,7 @@ Use the [code]process_material[/code] property to add a [ParticleProcessMaterial] to configure particle appearance and behavior. Alternatively, you can add a [ShaderMaterial] which will be applied to all particles. </description> <tutorials> + <link title="Particle systems (3D)">$DOCS_URL/tutorials/3d/particles/index.html</link> <link title="Controlling thousands of fish with Particles">$DOCS_URL/tutorials/performance/vertex_animation/controlling_thousands_of_fish.html</link> <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> @@ -18,6 +19,13 @@ Returns the axis-aligned bounding box that contains all the particles that are active in the current frame. </description> </method> + <method name="convert_from_particles"> + <return type="void" /> + <param index="0" name="particles" type="Node" /> + <description> + Sets this node's properties to match a given [CPUParticles3D] node. + </description> + </method> <method name="emit_particle"> <return type="void" /> <param index="0" name="xform" type="Transform3D" /> diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml index d728ab4f6c..990efbb3a4 100644 --- a/doc/classes/GeometryInstance3D.xml +++ b/doc/classes/GeometryInstance3D.xml @@ -7,6 +7,7 @@ Base node for geometry-based visual instances. Shares some common functionality like visibility and custom materials. </description> <tutorials> + <link title="Visibility ranges (HLOD)">$DOCS_URL/tutorials/3d/visibility_ranges.html</link> </tutorials> <methods> <method name="get_instance_shader_parameter" qualifiers="const"> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 1486990995..6451062fc5 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -333,6 +333,13 @@ Loads an image from the binary contents of a JPEG file. </description> </method> + <method name="load_ktx_from_buffer"> + <return type="int" enum="Error" /> + <param index="0" name="buffer" type="PackedByteArray" /> + <description> + Loads an image from the binary contents of a KTX file. + </description> + </method> <method name="load_png_from_buffer"> <return type="int" enum="Error" /> <param index="0" name="buffer" type="PackedByteArray" /> diff --git a/doc/classes/Label3D.xml b/doc/classes/Label3D.xml index abd0e179af..977fb00402 100644 --- a/doc/classes/Label3D.xml +++ b/doc/classes/Label3D.xml @@ -7,6 +7,7 @@ A node for displaying plain text in 3D space. By adjusting various properties of this node, you can configure things such as the text's appearance and whether it always faces the camera. </description> <tutorials> + <link title="3D text">$DOCS_URL/tutorials/3d/3d_text.html</link> </tutorials> <methods> <method name="generate_triangle_mesh" qualifiers="const"> diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml index ca9f821acf..ebb36ba92b 100644 --- a/doc/classes/Light3D.xml +++ b/doc/classes/Light3D.xml @@ -8,6 +8,7 @@ </description> <tutorials> <link title="3D lights and shadows">$DOCS_URL/tutorials/3d/lights_and_shadows.html</link> + <link title="Faking global illumination">$DOCS_URL/tutorials/3d/global_illumination/faking_global_illumination.html</link> <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index 6a6a72b329..a626c71377 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -12,6 +12,7 @@ [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> + <link title="Using Lightmap global illumination">$DOCS_URL/tutorials/3d/global_illumination/using_lightmap_gi.html</link> </tutorials> <members> <member name="bias" type="float" setter="set_bias" getter="get_bias" default="0.0005"> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index f5cb2e32a2..ef620da847 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -197,7 +197,7 @@ <member name="caret_force_displayed" type="bool" setter="set_caret_force_displayed" getter="is_caret_force_displayed" default="false"> If [code]true[/code], the [LineEdit] will always show the caret, even if focus is lost. </member> - <member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="true"> + <member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false"> Allow moving caret, selecting and removing the individual composite character components. [b]Note:[/b] [kbd]Backspace[/kbd] is always removing individual composite character components. </member> diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml index e61e68ddde..140f3ed74e 100644 --- a/doc/classes/MeshDataTool.xml +++ b/doc/classes/MeshDataTool.xml @@ -49,6 +49,7 @@ [b]Note:[/b] Godot uses clockwise [url=https://learnopengl.com/Advanced-OpenGL/Face-culling]winding order[/url] for front faces of triangle primitive modes. </description> <tutorials> + <link title="Using the MeshDataTool">$DOCS_URL/tutorials/3d/procedural_geometry/meshdatatool.html</link> </tutorials> <methods> <method name="clear"> diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index 0543b9abcf..f83dadfb15 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -12,8 +12,9 @@ [b]Note:[/b] Blend Shapes will be ignored if used in a MultiMesh. </description> <tutorials> - <link title="Animating thousands of fish with MultiMeshInstance">$DOCS_URL/tutorials/performance/vertex_animation/animating_thousands_of_fish.html</link> + <link title="Using MultiMeshInstance">$DOCS_URL/tutorials/3d/using_multi_mesh_instance.html</link> <link title="Optimization using MultiMeshes">$DOCS_URL/tutorials/performance/using_multimesh.html</link> + <link title="Animating thousands of fish with MultiMeshInstance">$DOCS_URL/tutorials/performance/vertex_animation/animating_thousands_of_fish.html</link> </tutorials> <methods> <method name="get_aabb" qualifiers="const"> diff --git a/doc/classes/MultiMeshInstance3D.xml b/doc/classes/MultiMeshInstance3D.xml index a50411cd63..65abd174ed 100644 --- a/doc/classes/MultiMeshInstance3D.xml +++ b/doc/classes/MultiMeshInstance3D.xml @@ -8,9 +8,9 @@ This is useful to optimize the rendering of a high number of instances of a given mesh (for example trees in a forest or grass strands). </description> <tutorials> - <link title="Animating thousands of fish with MultiMeshInstance">$DOCS_URL/tutorials/performance/vertex_animation/animating_thousands_of_fish.html</link> <link title="Using MultiMeshInstance">$DOCS_URL/tutorials/3d/using_multi_mesh_instance.html</link> <link title="Optimization using MultiMeshes">$DOCS_URL/tutorials/performance/using_multimesh.html</link> + <link title="Animating thousands of fish with MultiMeshInstance">$DOCS_URL/tutorials/performance/vertex_animation/animating_thousands_of_fish.html</link> </tutorials> <members> <member name="multimesh" type="MultiMesh" setter="set_multimesh" getter="get_multimesh"> diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index 464ffd50af..a76e739c23 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -7,8 +7,8 @@ A navigation mesh is a collection of polygons that define which areas of an environment are traversable to aid agents in pathfinding through complicated spaces. </description> <tutorials> - <link title="3D Navmesh Demo">https://godotengine.org/asset-library/asset/124</link> <link title="Using NavigationMeshes">$DOCS_URL/tutorials/navigation/navigation_using_navigationmeshes.html</link> + <link title="3D Navmesh Demo">https://godotengine.org/asset-library/asset/124</link> </tutorials> <methods> <method name="add_polygon"> diff --git a/doc/classes/NavigationObstacle2D.xml b/doc/classes/NavigationObstacle2D.xml index 09882da3b9..1eb18984a6 100644 --- a/doc/classes/NavigationObstacle2D.xml +++ b/doc/classes/NavigationObstacle2D.xml @@ -53,7 +53,7 @@ If [code]true[/code] the obstacle affects avoidance using agents. </member> <member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1"> - A bitfield determining the avoidance layers for this obstacle. Agent's with a matching bit on the their avoidance mask will avoid this obstacle. + A bitfield determining the avoidance layers for this obstacle. Agents with a matching bit on the their avoidance mask will avoid this obstacle. </member> <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.0"> Sets the avoidance radius for the obstacle. diff --git a/doc/classes/NavigationObstacle3D.xml b/doc/classes/NavigationObstacle3D.xml index 6f1649a457..141442eaf0 100644 --- a/doc/classes/NavigationObstacle3D.xml +++ b/doc/classes/NavigationObstacle3D.xml @@ -53,7 +53,7 @@ If [code]true[/code] the obstacle affects avoidance using agents. </member> <member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1"> - A bitfield determining the avoidance layers for this obstacle. Agent's with a matching bit on the their avoidance mask will avoid this obstacle. + A bitfield determining the avoidance layers for this obstacle. Agents with a matching bit on the their avoidance mask will avoid this obstacle. </member> <member name="height" type="float" setter="set_height" getter="get_height" default="1.0"> Sets the obstacle height used in 2D avoidance. 2D avoidance using agent's ignore obstacles that are below or above them. diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index fc349f3913..5fb1d7ab8f 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -38,7 +38,7 @@ [codeblocks] [gdscript] func _get(property): - if (property == "fake_property"): + if property == "fake_property": print("Getting my property!") return 4 @@ -212,9 +212,13 @@ Combined with [method _get] and [method _get_property_list], this method allows defining custom properties, which is particularly useful for editor plugins. Note that a property [i]must[/i] be present in [method get_property_list], otherwise this method will not be called. [codeblocks] [gdscript] + var internal_data = {} + func _set(property, value): - if (property == "fake_property"): - print("Setting my property to ", value) + if property == "fake_property": + # Storing the value in the fake property. + internal_data["fake_property"] = value + return true func _get_property_list(): return [ @@ -222,11 +226,14 @@ ] [/gdscript] [csharp] + private Godot.Collections.Dictionary _internalData = new Godot.Collections.Dictionary(); + public override void _Set(StringName property, Variant value) { if (property == "FakeProperty") { - GD.Print($"Setting my property to {value}"); + // Storing the value in the fake property. + _internalData["FakeProperty"] = value; return true; } @@ -611,8 +618,8 @@ <param index="1" name="default" type="Variant" default="null" /> <description> Returns the object's metadata value for the given entry [param name]. If the entry does not exist, returns [param default]. If [param default] is [code]null[/code], an error is also generated. - [b]Note:[/b] A metadata's [param name] must be a valid identifier as per [method StringName.is_valid_identifier] method. - [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited, although it can still be found by this method. + [b]Note:[/b] A metadata's name must be a valid identifier as per [method StringName.is_valid_identifier] method. + [b]Note:[/b] Metadata that has a name starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited, although it can still be found by this method. </description> </method> <method name="get_meta_list" qualifiers="const"> @@ -674,8 +681,8 @@ <param index="0" name="name" type="StringName" /> <description> Returns [code]true[/code] if a metadata entry is found with the given [param name]. See also [method get_meta], [method set_meta] and [method remove_meta]. - [b]Note:[/b] A metadata's [param name] must be a valid identifier as per [method StringName.is_valid_identifier] method. - [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited, although it can still be found by this method. + [b]Note:[/b] A metadata's name must be a valid identifier as per [method StringName.is_valid_identifier] method. + [b]Note:[/b] Metadata that has a name starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited, although it can still be found by this method. </description> </method> <method name="has_method" qualifiers="const"> @@ -802,8 +809,8 @@ <param index="0" name="name" type="StringName" /> <description> Removes the given entry [param name] from the object's metadata. See also [method has_meta], [method get_meta] and [method set_meta]. - [b]Note:[/b] A metadata's [param name] must be a valid identifier as per [method StringName.is_valid_identifier] method. - [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited, although it can still be found by this method. + [b]Note:[/b] A metadata's name must be a valid identifier as per [method StringName.is_valid_identifier] method. + [b]Note:[/b] Metadata that has a name starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited, although it can still be found by this method. </description> </method> <method name="set"> @@ -902,8 +909,8 @@ <description> Adds or changes the entry [param name] inside the object's metadata. The metadata [param value] can be any [Variant], although some types cannot be serialized correctly. If [param value] is [code]null[/code], the entry is removed. This is the equivalent of using [method remove_meta]. See also [method has_meta] and [method get_meta]. - [b]Note:[/b] A metadata's [param name] must be a valid identifier as per [method StringName.is_valid_identifier] method. - [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited, although it can still be found by this method. + [b]Note:[/b] A metadata's name must be a valid identifier as per [method StringName.is_valid_identifier] method. + [b]Note:[/b] Metadata that has a name starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited, although it can still be found by this method. </description> </method> <method name="set_script"> diff --git a/doc/classes/Occluder3D.xml b/doc/classes/Occluder3D.xml index b229343b40..56124cb053 100644 --- a/doc/classes/Occluder3D.xml +++ b/doc/classes/Occluder3D.xml @@ -8,6 +8,7 @@ See [OccluderInstance3D]'s documentation for instructions on setting up occlusion culling. </description> <tutorials> + <link title="Occlusion culling">$DOCS_URL/tutorials/3d/occlusion_culling.html</link> </tutorials> <methods> <method name="get_indices" qualifiers="const"> diff --git a/doc/classes/OccluderInstance3D.xml b/doc/classes/OccluderInstance3D.xml index 65b1bead9a..0dcc34f4d9 100644 --- a/doc/classes/OccluderInstance3D.xml +++ b/doc/classes/OccluderInstance3D.xml @@ -11,6 +11,7 @@ [b]Note:[/b] Occlusion culling is only effective if [member ProjectSettings.rendering/occlusion_culling/use_occlusion_culling] is [code]true[/code]. Enabling occlusion culling has a cost on the CPU. Only enable occlusion culling if you actually plan to use it. Large open scenes with few or no objects blocking the view will generally not benefit much from occlusion culling. Large open scenes generally benefit more from mesh LOD and visibility ranges ([member GeometryInstance3D.visibility_range_begin] and [member GeometryInstance3D.visibility_range_end]) compared to occlusion culling. </description> <tutorials> + <link title="Occlusion culling">$DOCS_URL/tutorials/3d/occlusion_culling.html</link> </tutorials> <methods> <method name="get_bake_mask_value" qualifiers="const"> diff --git a/doc/classes/OmniLight3D.xml b/doc/classes/OmniLight3D.xml index 44d1f8902e..f8de34e159 100644 --- a/doc/classes/OmniLight3D.xml +++ b/doc/classes/OmniLight3D.xml @@ -10,6 +10,7 @@ </description> <tutorials> <link title="3D lights and shadows">$DOCS_URL/tutorials/3d/lights_and_shadows.html</link> + <link title="Faking global illumination">$DOCS_URL/tutorials/3d/global_illumination/faking_global_illumination.html</link> </tutorials> <members> <member name="omni_attenuation" type="float" setter="set_param" getter="get_param" default="1.0"> diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index d2302fc27f..47a68802aa 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -6,9 +6,7 @@ <description> [OptionButton] is a type of button that brings up a dropdown with selectable items when pressed. The item selected becomes the "current" item and is displayed as the button text. See also [BaseButton] which contains common properties and methods associated with this node. - [b]Note:[/b] Properties [member Button.text] and [member Button.icon] are automatically set based on the selected item. They shouldn't be changed manually. [b]Note:[/b] The ID values used for items are limited to 32 bits, not full 64 bits of [int]. This has a range of [code]-2^32[/code] to [code]2^32 - 1[/code], i.e. [code]-2147483648[/code] to [code]2147483647[/code]. - [b]Note:[/b] The ID values used for items are 32-bit, unlike [int] which is always 64-bit. They go from [code]-2147483648[/code] to [code]2147483647[/code]. [b]Note:[/b] The [member Button.text] and [member Button.icon] properties are set automatically based on the selected item. They shouldn't be changed manually. </description> <tutorials> diff --git a/doc/classes/PolygonOccluder3D.xml b/doc/classes/PolygonOccluder3D.xml index a33722c38a..227f986f92 100644 --- a/doc/classes/PolygonOccluder3D.xml +++ b/doc/classes/PolygonOccluder3D.xml @@ -8,6 +8,7 @@ See [OccluderInstance3D]'s documentation for instructions on setting up occlusion culling. </description> <tutorials> + <link title="Occlusion culling">$DOCS_URL/tutorials/3d/occlusion_culling.html</link> </tutorials> <members> <member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array()"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index f426030f98..d5495ff7dc 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -43,8 +43,8 @@ var propertyInfo = new Godot.Collections.Dictionary { {"name", "category/propertyName"}, - {"type", Variant.Type.Int}, - {"hint", PropertyHint.Enum}, + {"type", (int)Variant.Type.Int}, + {"hint", (int)PropertyHint.Enum}, {"hint_string", "one,two,three"}, }; @@ -492,6 +492,9 @@ <member name="debug/gdscript/warnings/redundant_await" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function that is not a coroutine is called with await. </member> + <member name="debug/gdscript/warnings/redundant_for_variable_type" type="int" setter="" getter="" default="1"> + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a [code]for[/code] variable type specifier is a supertype of the inferred type. + </member> <member name="debug/gdscript/warnings/redundant_static_unload" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when the [code]@static_unload[/code] annotation is used in a script without any static variables. </member> diff --git a/doc/classes/QuadOccluder3D.xml b/doc/classes/QuadOccluder3D.xml index 717cc44bb5..fd28cbbaac 100644 --- a/doc/classes/QuadOccluder3D.xml +++ b/doc/classes/QuadOccluder3D.xml @@ -8,6 +8,7 @@ See [OccluderInstance3D]'s documentation for instructions on setting up occlusion culling. </description> <tutorials> + <link title="Occlusion culling">$DOCS_URL/tutorials/3d/occlusion_culling.html</link> </tutorials> <members> <member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2(1, 1)"> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 14f6235d08..5c81914511 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -11,7 +11,7 @@ [b]Note:[/b] [RenderingDevice] is not available when running in headless mode or when using the Compatibility rendering method. </description> <tutorials> - <link title="Using compute shaders">https://docs.godotengine.org/en/latest/tutorials/shaders/compute_shaders.html</link> + <link title="Using compute shaders">$DOCS_URL/tutorials/shaders/compute_shaders.html</link> </tutorials> <methods> <method name="barrier"> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index d972a2214a..7331a7fe32 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -4184,7 +4184,7 @@ <constant name="ARRAY_FLAG_USE_DYNAMIC_UPDATE" value="67108864" enum="ArrayFormat" is_bitfield="true"> </constant> <constant name="ARRAY_FLAG_USE_8_BONE_WEIGHTS" value="134217728" enum="ArrayFormat" is_bitfield="true"> - Flag used to mark that the array uses 8 bone weighs instead of 4. + Flag used to mark that the array uses 8 bone weights instead of 4. </constant> <constant name="ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY" value="268435456" enum="ArrayFormat" is_bitfield="true"> </constant> diff --git a/doc/classes/ResourceImporterCSVTranslation.xml b/doc/classes/ResourceImporterCSVTranslation.xml index e34f90d4ca..122728e75a 100644 --- a/doc/classes/ResourceImporterCSVTranslation.xml +++ b/doc/classes/ResourceImporterCSVTranslation.xml @@ -15,7 +15,7 @@ [/codeblock] </description> <tutorials> - <link title="Importing translations">https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_translations.html</link> + <link title="Importing translations">$DOCS_URL/tutorials/assets_pipeline/importing_translations.html</link> </tutorials> <members> <member name="compress" type="bool" setter="" getter="" default="true"> diff --git a/doc/classes/ResourceImporterLayeredTexture.xml b/doc/classes/ResourceImporterLayeredTexture.xml index 6d3c868e3d..e1e8ee710d 100644 --- a/doc/classes/ResourceImporterLayeredTexture.xml +++ b/doc/classes/ResourceImporterLayeredTexture.xml @@ -38,7 +38,7 @@ [b]VRAM Compressed:[/b] Reduced quality, low memory usage, low size on disk, slowest import. Only use for textures in 3D scenes, not for 2D elements. [b]VRAM Uncompressed:[/b] Original quality, high memory usage, highest size on disk, fastest import. [b]Basis Universal:[/b] Reduced quality, low memory usage, lowest size on disk, slow import. Only use for textures in 3D scenes, not for 2D elements. - See [url=https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_images.html#compress-mode]Compress mode[/url] in the manual for more details. + See [url=$DOCS_URL/tutorials/assets_pipeline/importing_images.html#compress-mode]Compress mode[/url] in the manual for more details. </member> <member name="mipmaps/generate" type="bool" setter="" getter="" default="true"> If [code]true[/code], smaller versions of the texture are generated on import. For example, a 64×64 texture will generate 6 mipmaps (32×32, 16×16, 8×8, 4×4, 2×2, 1×1). This has several benefits: diff --git a/doc/classes/ResourceImporterTexture.xml b/doc/classes/ResourceImporterTexture.xml index b81ebf383a..b6669b8293 100644 --- a/doc/classes/ResourceImporterTexture.xml +++ b/doc/classes/ResourceImporterTexture.xml @@ -37,7 +37,7 @@ [b]VRAM Compressed:[/b] Reduced quality, low memory usage, low size on disk, slowest import. Only use for textures in 3D scenes, not for 2D elements. [b]VRAM Uncompressed:[/b] Original quality, high memory usage, highest size on disk, fastest import. [b]Basis Universal:[/b] Reduced quality, low memory usage, lowest size on disk, slow import. Only use for textures in 3D scenes, not for 2D elements. - See [url=https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_images.html#compress-mode]Compress mode[/url] in the manual for more details. + See [url=$DOCS_URL/tutorials/assets_pipeline/importing_images.html#compress-mode]Compress mode[/url] in the manual for more details. </member> <member name="compress/normal_map" type="int" setter="" getter="" default="0"> When using a texture as normal map, only the red and green channels are required. Given regular texture compression algorithms produce artifacts that don't look that nice in normal maps, the RGTC compression format is the best fit for this data. Forcing this option to Enable will make Godot import the image as RGTC compressed. By default, it's set to Detect. This means that if the texture is ever detected to be used as a normal map, it will be changed to Enable and reimported automatically. diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index b951566707..c41072b20c 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -380,6 +380,13 @@ Adds a [code][font][/code] tag with an italics font to the tag stack. This is the same as adding an [code][i][/code] tag if not currently in a [code][b][/code] tag. </description> </method> + <method name="push_language"> + <return type="void" /> + <param index="0" name="language" type="String" /> + <description> + Adds language code used for text shaping algorithm and Open-Type font features. + </description> + </method> <method name="push_list"> <return type="void" /> <param index="0" name="level" type="int" /> diff --git a/doc/classes/SphereOccluder3D.xml b/doc/classes/SphereOccluder3D.xml index a154b2742c..bda8f4c978 100644 --- a/doc/classes/SphereOccluder3D.xml +++ b/doc/classes/SphereOccluder3D.xml @@ -8,6 +8,7 @@ See [OccluderInstance3D]'s documentation for instructions on setting up occlusion culling. </description> <tutorials> + <link title="Occlusion culling">$DOCS_URL/tutorials/3d/occlusion_culling.html</link> </tutorials> <members> <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0"> diff --git a/doc/classes/SpotLight3D.xml b/doc/classes/SpotLight3D.xml index 06d3dfcef7..e89c99db1c 100644 --- a/doc/classes/SpotLight3D.xml +++ b/doc/classes/SpotLight3D.xml @@ -10,6 +10,7 @@ </description> <tutorials> <link title="3D lights and shadows">$DOCS_URL/tutorials/3d/lights_and_shadows.html</link> + <link title="Faking global illumination">$DOCS_URL/tutorials/3d/global_illumination/faking_global_illumination.html</link> <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <members> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 4f752ec417..494f41422f 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -28,6 +28,7 @@ [b]Note:[/b] Godot uses clockwise [url=https://learnopengl.com/Advanced-OpenGL/Face-culling]winding order[/url] for front faces of triangle primitive modes. </description> <tutorials> + <link title="Using the SurfaceTool">$DOCS_URL/tutorials/3d/procedural_geometry/surfacetool.html</link> <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> </tutorials> <methods> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index e611b7e3fa..d22e646dd0 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -1100,7 +1100,7 @@ <member name="caret_draw_when_editable_disabled" type="bool" setter="set_draw_caret_when_editable_disabled" getter="is_drawing_caret_when_editable_disabled" default="false"> If [code]true[/code], caret will be visible when [member editable] is disabled. </member> - <member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="true"> + <member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false"> Allow moving caret, selecting and removing the individual composite character components. [b]Note:[/b] [kbd]Backspace[/kbd] is always removing individual composite character components. </member> diff --git a/doc/classes/TextMesh.xml b/doc/classes/TextMesh.xml index 1814b474a4..00c6b0b1a8 100644 --- a/doc/classes/TextMesh.xml +++ b/doc/classes/TextMesh.xml @@ -9,6 +9,7 @@ The UV layout is arranged in 4 horizontal strips, top to bottom: 40% of the height for the front face, 40% for the back face, 10% for the outer edges and 10% for the inner edges. </description> <tutorials> + <link title="3D text">$DOCS_URL/tutorials/3d/3d_text.html</link> </tutorials> <members> <member name="autowrap_mode" type="int" setter="set_autowrap_mode" getter="get_autowrap_mode" enum="TextServer.AutowrapMode" default="0"> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index e3afe6f65d..0646a70e73 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -200,7 +200,7 @@ <param index="2" name="char" type="int" /> <param index="3" name="variation_selector" type="int" /> <description> - Returns the glyph index of a [param char], optionally modified by the [param variation_selector]. See [method font_get_char_from_glyph_index]. + Returns the glyph index of a [param char], optionally modified by the [param variation_selector]. See [method font_get_char_from_glyph_index]. </description> </method> <method name="font_get_glyph_list" qualifiers="const"> @@ -1139,6 +1139,14 @@ Clears text buffer (removes text and inline objects). </description> </method> + <method name="shaped_text_closest_character_pos" qualifiers="const"> + <return type="int" /> + <param index="0" name="shaped" type="RID" /> + <param index="1" name="pos" type="int" /> + <description> + Returns composite character position closest to the [param pos]. + </description> + </method> <method name="shaped_text_draw" qualifiers="const"> <return type="void" /> <param index="0" name="shaped" type="RID" /> @@ -1189,6 +1197,13 @@ Returns shapes of the carets corresponding to the character offset [param position] in the text. Returned caret shape is 1 pixel wide rectangle. </description> </method> + <method name="shaped_text_get_character_breaks" qualifiers="const"> + <return type="PackedInt32Array" /> + <param index="0" name="shaped" type="RID" /> + <description> + Returns array of the composite character boundaries. + </description> + </method> <method name="shaped_text_get_custom_punctuation" qualifiers="const"> <return type="String" /> <param index="0" name="shaped" type="RID" /> @@ -1406,7 +1421,7 @@ <return type="bool" /> <param index="0" name="shaped" type="RID" /> <description> - Returns [code]true[/code], if text buffer contents any visible characters. + Returns [code]true[/code] if text buffer contains any visible characters. </description> </method> <method name="shaped_text_hit_test_grapheme" qualifiers="const"> @@ -1432,7 +1447,7 @@ Returns [code]true[/code] if buffer is successfully shaped. </description> </method> - <method name="shaped_text_next_grapheme_pos" qualifiers="const"> + <method name="shaped_text_next_character_pos" qualifiers="const"> <return type="int" /> <param index="0" name="shaped" type="RID" /> <param index="1" name="pos" type="int" /> @@ -1440,6 +1455,14 @@ Returns composite character end position closest to the [param pos]. </description> </method> + <method name="shaped_text_next_grapheme_pos" qualifiers="const"> + <return type="int" /> + <param index="0" name="shaped" type="RID" /> + <param index="1" name="pos" type="int" /> + <description> + Returns grapheme end position closest to the [param pos]. + </description> + </method> <method name="shaped_text_overrun_trim_to_width"> <return type="void" /> <param index="0" name="shaped" type="RID" /> @@ -1449,7 +1472,7 @@ Trims text if it exceeds the given width. </description> </method> - <method name="shaped_text_prev_grapheme_pos" qualifiers="const"> + <method name="shaped_text_prev_character_pos" qualifiers="const"> <return type="int" /> <param index="0" name="shaped" type="RID" /> <param index="1" name="pos" type="int" /> @@ -1457,6 +1480,14 @@ Returns composite character start position closest to the [param pos]. </description> </method> + <method name="shaped_text_prev_grapheme_pos" qualifiers="const"> + <return type="int" /> + <param index="0" name="shaped" type="RID" /> + <param index="1" name="pos" type="int" /> + <description> + Returns grapheme start position closest to the [param pos]. + </description> + </method> <method name="shaped_text_resize_object"> <return type="bool" /> <param index="0" name="shaped" type="RID" /> @@ -1568,6 +1599,18 @@ [b]Note:[/b] Always returns [code]false[/code] if the server does not support the [constant FEATURE_UNICODE_SECURITY] feature. </description> </method> + <method name="string_get_character_breaks" qualifiers="const"> + <return type="PackedInt32Array" /> + <param index="0" name="string" type="String" /> + <param index="1" name="language" type="String" default="""" /> + <description> + Returns array of the composite character boundaries. + [codeblock] + var ts = TextServerManager.get_primary_interface() + print(ts.string_get_word_breaks("Test ❤️🔥 Test")) # Prints [1, 2, 3, 4, 5, 9, 10, 11, 12, 13, 14] + [/codeblock] + </description> + </method> <method name="string_get_word_breaks" qualifiers="const"> <return type="PackedInt32Array" /> <param index="0" name="string" type="String" /> diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index fc0a424187..c58e3b705f 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -981,6 +981,13 @@ <description> </description> </method> + <method name="_shaped_text_closest_character_pos" qualifiers="virtual const"> + <return type="int" /> + <param index="0" name="shaped" type="RID" /> + <param index="1" name="pos" type="int" /> + <description> + </description> + </method> <method name="_shaped_text_draw" qualifiers="virtual const"> <return type="void" /> <param index="0" name="shaped" type="RID" /> @@ -1026,6 +1033,12 @@ <description> </description> </method> + <method name="_shaped_text_get_character_breaks" qualifiers="virtual const"> + <return type="PackedInt32Array" /> + <param index="0" name="shaped" type="RID" /> + <description> + </description> + </method> <method name="_shaped_text_get_custom_punctuation" qualifiers="virtual const"> <return type="String" /> <param index="0" name="shaped" type="RID" /> @@ -1229,6 +1242,13 @@ <description> </description> </method> + <method name="_shaped_text_next_character_pos" qualifiers="virtual const"> + <return type="int" /> + <param index="0" name="shaped" type="RID" /> + <param index="1" name="pos" type="int" /> + <description> + </description> + </method> <method name="_shaped_text_next_grapheme_pos" qualifiers="virtual const"> <return type="int" /> <param index="0" name="shaped" type="RID" /> @@ -1244,6 +1264,13 @@ <description> </description> </method> + <method name="_shaped_text_prev_character_pos" qualifiers="virtual const"> + <return type="int" /> + <param index="0" name="shaped" type="RID" /> + <param index="1" name="pos" type="int" /> + <description> + </description> + </method> <method name="_shaped_text_prev_grapheme_pos" qualifiers="virtual const"> <return type="int" /> <param index="0" name="shaped" type="RID" /> @@ -1356,6 +1383,13 @@ <description> </description> </method> + <method name="_string_get_character_breaks" qualifiers="virtual const"> + <return type="PackedInt32Array" /> + <param index="0" name="string" type="String" /> + <param index="1" name="language" type="String" /> + <description> + </description> + </method> <method name="_string_get_word_breaks" qualifiers="virtual const"> <return type="PackedInt32Array" /> <param index="0" name="string" type="String" /> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 004e93ac04..d85678cc81 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -589,7 +589,7 @@ The right margin of the scrollbars. When negative, uses [theme_item panel] right margin. </theme_item> <theme_item name="scrollbar_margin_top" data_type="constant" type="int" default="-1"> - The right margin of the vertical scrollbar. When negative, uses [theme_item panel] top margin. + The top margin of the vertical scrollbar. When negative, uses [theme_item panel] top margin. </theme_item> <theme_item name="scrollbar_v_separation" data_type="constant" type="int" default="4"> The vertical separation of tree content and scrollbar. diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index 865571263e..597a363514 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -339,6 +339,13 @@ Returns item's text base writing direction. </description> </method> + <method name="get_text_overrun_behavior" qualifiers="const"> + <return type="int" enum="TextServer.OverrunBehavior" /> + <param index="0" name="column" type="int" /> + <description> + Returns the clipping behavior when the text exceeds the item's bounding rectangle in the given [param column]. By default it is [constant TextServer.OVERRUN_TRIM_ELLIPSIS]. + </description> + </method> <method name="get_tooltip_text" qualifiers="const"> <return type="String" /> <param index="0" name="column" type="int" /> @@ -728,6 +735,14 @@ Sets item's text base writing direction. </description> </method> + <method name="set_text_overrun_behavior"> + <return type="void" /> + <param index="0" name="column" type="int" /> + <param index="1" name="overrun_behavior" type="int" enum="TextServer.OverrunBehavior" /> + <description> + Sets the clipping behavior when the text exceeds the item's bounding rectangle in the given [param column]. + </description> + </method> <method name="set_tooltip_text"> <return type="void" /> <param index="0" name="column" type="int" /> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 7645b249e3..0ecfe53123 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -90,7 +90,7 @@ Some [Tweener]s use transitions and eases. The first accepts a [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum EaseType] constant, and controls where the [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best. [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/tween_cheatsheet.webp]Tween easing and transition types cheatsheet[/url] [b]Note:[/b] Tweens are not designed to be re-used and trying to do so results in an undefined behavior. Create a new Tween for each animation and every time you replay an animation from start. Keep in mind that Tweens start immediately, so only create a Tween when you want to start animating. - [b]Note:[/b] The tween is processed after all of the nodes in the current frame, i.e. node's [method Node._process] method would be called before the timer (or [method Node._physics_process] depending on the value passed to [method set_process_mode]). + [b]Note:[/b] The tween is processed after all of the nodes in the current frame, i.e. node's [method Node._process] method would be called before the tween (or [method Node._physics_process] depending on the value passed to [method set_process_mode]). </description> <tutorials> </tutorials> diff --git a/doc/classes/VideoStream.xml b/doc/classes/VideoStream.xml index 600cc3f67e..aa4c91bac2 100644 --- a/doc/classes/VideoStream.xml +++ b/doc/classes/VideoStream.xml @@ -7,6 +7,7 @@ Base resource type for all video streams. Classes that derive from [VideoStream] can all be used as resource types to play back videos in [VideoStreamPlayer]. </description> <tutorials> + <link title="Playing videos">$DOCS_URL/tutorials/animation/playing_videos.html</link> </tutorials> <methods> <method name="_instantiate_playback" qualifiers="virtual"> diff --git a/doc/classes/VideoStreamPlayer.xml b/doc/classes/VideoStreamPlayer.xml index 19b1e20e5b..0d8776b0b7 100644 --- a/doc/classes/VideoStreamPlayer.xml +++ b/doc/classes/VideoStreamPlayer.xml @@ -10,6 +10,7 @@ [b]Warning:[/b] On Web, video playback [i]will[/i] perform poorly due to missing architecture-specific assembly optimizations. </description> <tutorials> + <link title="Playing videos">$DOCS_URL/tutorials/animation/playing_videos.html</link> </tutorials> <methods> <method name="get_stream_length" qualifiers="const"> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 101717966e..43640916f4 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -298,10 +298,12 @@ </member> <member name="physics_object_picking" type="bool" setter="set_physics_object_picking" getter="get_physics_object_picking" default="false"> If [code]true[/code], the objects rendered by viewport become subjects of mouse picking process. + [b]Note:[/b] The number of simultaneously pickable objects is limited to 64 and they are selected in a non-deterministic order, which can be different in each picking process. </member> <member name="physics_object_picking_sort" type="bool" setter="set_physics_object_picking_sort" getter="get_physics_object_picking_sort" default="false"> If [code]true[/code], objects receive mouse picking events sorted primarily by their [member CanvasItem.z_index] and secondarily by their position in the scene tree. If [code]false[/code], the order is undetermined. [b]Note:[/b] This setting is disabled by default because of its potential expensive computational cost. + [b]Note:[/b] Sorting happens after selecting the pickable objects. Because of the limitation of 64 simultaneously pickable objects, it is not guaranteed that the object with the highest [member CanvasItem.z_index] receives the picking event. </member> <member name="positional_shadow_atlas_16_bits" type="bool" setter="set_positional_shadow_atlas_16_bits" getter="get_positional_shadow_atlas_16_bits" default="true"> Use 16 bits for the omni/spot shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices. diff --git a/doc/classes/VoxelGI.xml b/doc/classes/VoxelGI.xml index f5fd51bef7..9e4026a750 100644 --- a/doc/classes/VoxelGI.xml +++ b/doc/classes/VoxelGI.xml @@ -11,7 +11,7 @@ [b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh. To further prevent light leaks, you can also strategically place temporary [MeshInstance3D] nodes with their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_STATIC]. These temporary nodes can then be hidden after baking the [VoxelGI] node. </description> <tutorials> - <link title="VoxelGI">$DOCS_URL/tutorials/3d/global_illumination/using_voxel_gi.html</link> + <link title="Using Voxel global illumination">$DOCS_URL/tutorials/3d/global_illumination/using_voxel_gi.html</link> <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 92cd11d720..b03ef3ab4e 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -351,6 +351,12 @@ Returns [code]true[/code] if font oversampling is enabled. See [method set_use_font_oversampling]. </description> </method> + <method name="move_to_center"> + <return type="void" /> + <description> + Centers a native window on the current screen and an embedded window on its embedder [Viewport]. + </description> + </method> <method name="move_to_foreground"> <return type="void" /> <description> diff --git a/doc/classes/X509Certificate.xml b/doc/classes/X509Certificate.xml index 9cc4060852..a4148792e5 100644 --- a/doc/classes/X509Certificate.xml +++ b/doc/classes/X509Certificate.xml @@ -8,6 +8,7 @@ They can be used as the server certificate in [method StreamPeerTLS.accept_stream] (along with the proper [CryptoKey]), and to specify the only certificate that should be accepted when connecting to a TLS server via [method StreamPeerTLS.connect_to_stream]. </description> <tutorials> + <link title="SSL certificates">$DOCS_URL/tutorials/networking/ssl_certificates.html</link> </tutorials> <methods> <method name="load"> diff --git a/doc/classes/XRAnchor3D.xml b/doc/classes/XRAnchor3D.xml index e9d9798de8..9390d5c545 100644 --- a/doc/classes/XRAnchor3D.xml +++ b/doc/classes/XRAnchor3D.xml @@ -9,6 +9,7 @@ Keep in mind that, as long as plane detection is enabled, the size, placing and orientation of an anchor will be updated as the detection logic learns more about the real world out there especially if only part of the surface is in view. </description> <tutorials> + <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link> </tutorials> <methods> <method name="get_plane" qualifiers="const"> diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml index b933abd182..ea2bbf4cfb 100644 --- a/doc/classes/XRInterfaceExtension.xml +++ b/doc/classes/XRInterfaceExtension.xml @@ -7,6 +7,7 @@ External XR interface plugins should inherit from this class. </description> <tutorials> + <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link> </tutorials> <methods> <method name="_end_frame" qualifiers="virtual"> diff --git a/doc/classes/XRNode3D.xml b/doc/classes/XRNode3D.xml index 9c0955b0b1..f9ea540e2e 100644 --- a/doc/classes/XRNode3D.xml +++ b/doc/classes/XRNode3D.xml @@ -7,6 +7,7 @@ This node can be bound to a specific pose of a [XRPositionalTracker] and will automatically have its [member Node3D.transform] updated by the [XRServer]. Nodes of this type must be added as children of the [XROrigin3D] node. </description> <tutorials> + <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link> </tutorials> <methods> <method name="get_has_tracking_data" qualifiers="const"> diff --git a/doc/classes/XRPose.xml b/doc/classes/XRPose.xml index 51f7253a17..f7bfcf4e38 100644 --- a/doc/classes/XRPose.xml +++ b/doc/classes/XRPose.xml @@ -8,6 +8,7 @@ Orientation, location, linear velocity and angular velocity are all provided for each pose by the XR runtime. This object contains this state of a pose. </description> <tutorials> + <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link> </tutorials> <methods> <method name="get_adjusted_transform" qualifiers="const"> diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 9f8d9164ce..0156876368 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1451,7 +1451,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { void RasterizerCanvasGLES3::_add_to_batch(uint32_t &r_index, bool &r_batch_broken) { state.canvas_instance_batches[state.current_batch_index].instance_count++; r_index++; - if (r_index >= data.max_instances_per_buffer) { + if (r_index + state.last_item_index >= data.max_instances_per_buffer) { // Copy over all data needed for rendering right away // then go back to recording item commands. glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.current_instance_buffer_index]); diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 381cf1a7c6..a96a491517 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -1689,14 +1689,16 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b // Color and custom need to be packed so copy buffer to data_cache and pack. _multimesh_make_local(multimesh); - multimesh->data_cache = p_buffer; - float *w = multimesh->data_cache.ptrw(); uint32_t old_stride = multimesh->xform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12; old_stride += multimesh->uses_colors ? 4 : 0; old_stride += multimesh->uses_custom_data ? 4 : 0; ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)old_stride)); + multimesh->data_cache = p_buffer; + + float *w = multimesh->data_cache.ptrw(); + for (int i = 0; i < multimesh->instances; i++) { { float *dataptr = w + i * old_stride; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 59b1d176c6..b955fc2fca 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -2622,7 +2622,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co vkCmdCopyBufferToImage(command_buffer, staging_buffer_blocks[staging_buffer_current].buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy); - staging_buffer_blocks.write[staging_buffer_current].fill_amount += alloc_size; + staging_buffer_blocks.write[staging_buffer_current].fill_amount = alloc_offset + alloc_size; } } } diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 146b5a794c..36b19198e6 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -31,6 +31,7 @@ #include "editor/action_map_editor.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "editor/event_listener_line_edit.h" #include "editor/input_event_configuration_dialog.h" #include "scene/gui/check_button.h" @@ -224,6 +225,7 @@ void ActionMapEditor::_tree_item_activated() { void ActionMapEditor::set_show_builtin_actions(bool p_show) { show_builtin_actions = p_show; show_builtin_actions_checkbutton->set_pressed(p_show); + EditorSettings::get_singleton()->set_project_metadata("project_settings", "show_builtin_actions", show_builtin_actions); // Prevent unnecessary updates of action list when cache is empty. if (!actions_cache.is_empty()) { @@ -568,11 +570,13 @@ ActionMapEditor::ActionMapEditor() { _add_edit_text_changed(add_edit->get_text()); show_builtin_actions_checkbutton = memnew(CheckButton); - show_builtin_actions_checkbutton->set_pressed(false); show_builtin_actions_checkbutton->set_text(TTR("Show Built-in Actions")); show_builtin_actions_checkbutton->connect("toggled", callable_mp(this, &ActionMapEditor::set_show_builtin_actions)); add_hbox->add_child(show_builtin_actions_checkbutton); + show_builtin_actions = EditorSettings::get_singleton()->get_project_metadata("project_settings", "show_builtin_actions", false); + show_builtin_actions_checkbutton->set_pressed(show_builtin_actions); + main_vbox->add_child(add_hbox); // Action Editor Tree diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index afa6aaf395..ff59911159 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -1347,20 +1347,20 @@ void ScriptEditorDebugger::_live_edit_set() { NodePath np = path; - EditorNode::get_singleton()->get_editor_data().set_edited_scene_live_edit_root(np); + EditorNode::get_editor_data().set_edited_scene_live_edit_root(np); update_live_edit_root(); } void ScriptEditorDebugger::_live_edit_clear() { NodePath np = NodePath("/root"); - EditorNode::get_singleton()->get_editor_data().set_edited_scene_live_edit_root(np); + EditorNode::get_editor_data().set_edited_scene_live_edit_root(np); update_live_edit_root(); } void ScriptEditorDebugger::update_live_edit_root() { - NodePath np = EditorNode::get_singleton()->get_editor_data().get_edited_scene_live_edit_root(); + NodePath np = EditorNode::get_editor_data().get_edited_scene_live_edit_root(); Array msg; msg.push_back(np); diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 0b2c2bea15..5ea5553cd3 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -244,7 +244,7 @@ EditorSelectionHistory::EditorSelectionHistory() { //////////////////////////////////////////////////////////// -EditorPlugin *EditorData::get_editor(Object *p_object) { +EditorPlugin *EditorData::get_handling_main_editor(Object *p_object) { // We need to iterate backwards so that we can check user-created plugins first. // Otherwise, it would not be possible for plugins to handle CanvasItem and Spatial nodes. for (int i = editor_plugins.size() - 1; i > -1; i--) { @@ -256,7 +256,7 @@ EditorPlugin *EditorData::get_editor(Object *p_object) { return nullptr; } -Vector<EditorPlugin *> EditorData::get_subeditors(Object *p_object) { +Vector<EditorPlugin *> EditorData::get_handling_sub_editors(Object *p_object) { Vector<EditorPlugin *> sub_plugins; for (int i = editor_plugins.size() - 1; i > -1; i--) { if (!editor_plugins[i]->has_main_screen() && editor_plugins[i]->handles(p_object)) { @@ -266,7 +266,7 @@ Vector<EditorPlugin *> EditorData::get_subeditors(Object *p_object) { return sub_plugins; } -EditorPlugin *EditorData::get_editor(String p_name) { +EditorPlugin *EditorData::get_editor_by_name(String p_name) { for (int i = editor_plugins.size() - 1; i > -1; i--) { if (editor_plugins[i]->get_name() == p_name) { return editor_plugins[i]; diff --git a/editor/editor_data.h b/editor/editor_data.h index 28fe13e537..01e9dc4b07 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -150,9 +150,9 @@ private: Ref<Texture2D> _load_script_icon(const String &p_path) const; public: - EditorPlugin *get_editor(Object *p_object); - Vector<EditorPlugin *> get_subeditors(Object *p_object); - EditorPlugin *get_editor(String p_name); + EditorPlugin *get_handling_main_editor(Object *p_object); + Vector<EditorPlugin *> get_handling_sub_editors(Object *p_object); + EditorPlugin *get_editor_by_name(String p_name); void copy_object_params(Object *p_object); void paste_object_params(Object *p_object); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 7ac812101a..ae3cd35fca 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -880,8 +880,8 @@ void EditorProperty::_update_pin_flags() { // Avoid errors down the road by ignoring nodes which are not part of a scene if (!node->get_owner()) { bool is_scene_root = false; - for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_edited_scene_count(); ++i) { - if (EditorNode::get_singleton()->get_editor_data().get_edited_scene_root(i) == node) { + for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); ++i) { + if (EditorNode::get_editor_data().get_edited_scene_root(i) == node) { is_scene_root = true; break; } @@ -1719,7 +1719,7 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) { undo_redo->create_action(action_name); if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) { // Call the function. - Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name()); + Callable move_function = EditorNode::get_editor_data().get_move_array_element_function(object->get_class_name()); if (move_function.is_valid()) { Variant args[] = { undo_redo, object, array_element_prefix, p_element_index, p_to_pos }; const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] }; @@ -1864,7 +1864,7 @@ void EditorInspectorArray::_clear_array() { if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) { for (int i = count - 1; i >= 0; i--) { // Call the function. - Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name()); + Callable move_function = EditorNode::get_editor_data().get_move_array_element_function(object->get_class_name()); if (move_function.is_valid()) { Variant args[] = { undo_redo, object, array_element_prefix, i, -1 }; const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] }; @@ -1918,7 +1918,7 @@ void EditorInspectorArray::_resize_array(int p_size) { if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) { for (int i = count; i < p_size; i++) { // Call the function. - Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name()); + Callable move_function = EditorNode::get_editor_data().get_move_array_element_function(object->get_class_name()); if (move_function.is_valid()) { Variant args[] = { undo_redo, object, array_element_prefix, -1, -1 }; const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] }; @@ -1937,7 +1937,7 @@ void EditorInspectorArray::_resize_array(int p_size) { if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) { for (int i = count - 1; i > p_size - 1; i--) { // Call the function. - Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name()); + Callable move_function = EditorNode::get_editor_data().get_move_array_element_function(object->get_class_name()); if (move_function.is_valid()) { Variant args[] = { undo_redo, object, array_element_prefix, i, -1 }; const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] }; @@ -3393,6 +3393,7 @@ void EditorInspector::edit(Object *p_object) { if (object == p_object) { return; } + if (object) { _clear(); object->disconnect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); @@ -3500,9 +3501,12 @@ void EditorInspector::_filter_changed(const String &p_text) { update_tree(); } -void EditorInspector::set_use_folding(bool p_enable) { - use_folding = p_enable; - update_tree(); +void EditorInspector::set_use_folding(bool p_use_folding, bool p_update_tree) { + use_folding = p_use_folding; + + if (p_update_tree) { + update_tree(); + } } bool EditorInspector::is_using_folding() { @@ -3693,7 +3697,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo Variant v_undo_redo = undo_redo; Variant v_object = object; Variant v_name = p_name; - const Vector<Callable> &callbacks = EditorNode::get_singleton()->get_editor_data().get_undo_redo_inspector_hook_callback(); + const Vector<Callable> &callbacks = EditorNode::get_editor_data().get_undo_redo_inspector_hook_callback(); for (int i = 0; i < callbacks.size(); i++) { const Callable &callback = callbacks[i]; diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index ed0d0ec373..e41f18e350 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -589,7 +589,7 @@ public: void set_use_filter(bool p_use); void register_text_enter(Node *p_line_edit); - void set_use_folding(bool p_enable); + void set_use_folding(bool p_use_folding, bool p_update_tree = true); bool is_using_folding(); void collapse_all_folding(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index c6f5a6082b..d6dea2dc79 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -56,7 +56,6 @@ #include "scene/gui/popup.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/split_container.h" -#include "scene/gui/tab_bar.h" #include "scene/gui/tab_container.h" #include "scene/main/window.h" #include "scene/property_utils.h" @@ -105,6 +104,7 @@ #include "editor/filesystem_dock.h" #include "editor/gui/editor_file_dialog.h" #include "editor/gui/editor_run_bar.h" +#include "editor/gui/editor_scene_tabs.h" #include "editor/gui/editor_title_bar.h" #include "editor/gui/editor_toaster.h" #include "editor/history_dock.h" @@ -271,90 +271,6 @@ void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vecto } } -// TODO: This REALLY should be done in a better way than replacing all tabs after almost EVERY action. -void EditorNode::_update_scene_tabs() { - bool show_rb = EDITOR_GET("interface/scene_tabs/show_script_button"); - - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { - DisplayServer::get_singleton()->global_menu_clear("_dock"); - } - - // Get all scene names, which may be ambiguous. - Vector<String> disambiguated_scene_names; - Vector<String> full_path_names; - for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { - disambiguated_scene_names.append(editor_data.get_scene_title(i)); - full_path_names.append(editor_data.get_scene_path(i)); - } - - disambiguate_filenames(full_path_names, disambiguated_scene_names); - - // Workaround to ignore the tab_changed signal from the first added tab. - scene_tabs->disconnect("tab_changed", callable_mp(this, &EditorNode::_scene_tab_changed)); - - scene_tabs->clear_tabs(); - Ref<Texture2D> script_icon = gui_base->get_theme_icon(SNAME("Script"), SNAME("EditorIcons")); - for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { - Node *type_node = editor_data.get_edited_scene_root(i); - Ref<Texture2D> icon; - if (type_node) { - icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node"); - } - - bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(i)); - scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon); - - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { - DisplayServer::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), callable_mp(this, &EditorNode::_global_menu_scene), Callable(), i); - } - - if (show_rb && editor_data.get_scene_root_script(i).is_valid()) { - scene_tabs->set_tab_button_icon(i, script_icon); - } - } - - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { - DisplayServer::get_singleton()->global_menu_add_separator("_dock"); - DisplayServer::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), callable_mp(this, &EditorNode::_global_menu_new_window)); - } - - if (scene_tabs->get_tab_count() > 0) { - scene_tabs->set_current_tab(editor_data.get_edited_scene()); - } - - const Size2 add_button_size = Size2(scene_tab_add->get_size().x, scene_tabs->get_size().y); - if (scene_tabs->get_offset_buttons_visible()) { - // Move the add button to a fixed position. - if (scene_tab_add->get_parent() == scene_tabs) { - scene_tabs->remove_child(scene_tab_add); - scene_tab_add_ph->add_child(scene_tab_add); - scene_tab_add->set_rect(Rect2(Point2(), add_button_size)); - } - } else { - // Move the add button to be after the last tab. - if (scene_tab_add->get_parent() == scene_tab_add_ph) { - scene_tab_add_ph->remove_child(scene_tab_add); - scene_tabs->add_child(scene_tab_add); - } - - if (scene_tabs->get_tab_count() == 0) { - scene_tab_add->set_rect(Rect2(Point2(), add_button_size)); - return; - } - - Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1); - int hsep = scene_tabs->get_theme_constant(SNAME("h_separation")); - if (scene_tabs->is_layout_rtl()) { - scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - add_button_size.x - hsep, last_tab.position.y), add_button_size)); - } else { - scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y), add_button_size)); - } - } - - // Reconnect after everything is done. - scene_tabs->connect("tab_changed", callable_mp(this, &EditorNode::_scene_tab_changed)); -} - void EditorNode::_version_control_menu_option(int p_idx) { switch (vcs_actions_menu->get_item_id(p_idx)) { case RUN_VCS_METADATA: { @@ -391,16 +307,6 @@ void EditorNode::shortcut_input(const Ref<InputEvent> &p_event) { if ((k.is_valid() && k->is_pressed() && !k->is_echo()) || Object::cast_to<InputEventShortcut>(*p_event)) { EditorPlugin *old_editor = editor_plugin_screen; - if (ED_IS_SHORTCUT("editor/next_tab", p_event)) { - int next_tab = editor_data.get_edited_scene() + 1; - next_tab %= editor_data.get_edited_scene_count(); - _scene_tab_changed(next_tab); - } - if (ED_IS_SHORTCUT("editor/prev_tab", p_event)) { - int next_tab = editor_data.get_edited_scene() - 1; - next_tab = next_tab >= 0 ? next_tab : editor_data.get_edited_scene_count() - 1; - _scene_tab_changed(next_tab); - } if (ED_IS_SHORTCUT("editor/filter_files", p_event)) { FileSystemDock::get_singleton()->focus_on_filter(); } @@ -431,6 +337,8 @@ void EditorNode::shortcut_input(const Ref<InputEvent> &p_event) { } void EditorNode::_update_from_settings() { + _update_title(); + int current_filter = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_filter"); if (current_filter != scene_root->get_default_canvas_item_texture_filter()) { Viewport::DefaultCanvasItemTextureFilter tf = (Viewport::DefaultCanvasItemTextureFilter)current_filter; @@ -509,6 +417,8 @@ void EditorNode::_update_from_settings() { tree->set_debug_collisions_color(GLOBAL_GET("debug/shapes/collision/shape_color")); tree->set_debug_collision_contact_color(GLOBAL_GET("debug/shapes/collision/contact_color")); + ResourceImporterTexture::get_singleton()->update_imports(); + #ifdef DEBUG_ENABLED NavigationServer3D::get_singleton()->set_debug_navigation_edge_connection_color(GLOBAL_GET("debug/shapes/navigation/edge_connection_color")); NavigationServer3D::get_singleton()->set_debug_navigation_geometry_edge_color(GLOBAL_GET("debug/shapes/navigation/geometry_edge_color")); @@ -558,7 +468,7 @@ void EditorNode::_notification(int p_what) { } if (editor_data.is_scene_changed(-1)) { - _update_scene_tabs(); + scene_tabs->update_scene_tabs(); } // Update the animation frame of the update spinner. @@ -584,18 +494,6 @@ void EditorNode::_notification(int p_what) { ResourceImporterTexture::get_singleton()->update_imports(); - if (settings_changed) { - _update_title(); - } - - if (settings_changed) { - _update_from_settings(); - settings_changed = false; - emit_signal(SNAME("project_settings_changed")); - } - - ResourceImporterTexture::get_singleton()->update_imports(); - bottom_panel_updating = false; } break; @@ -715,7 +613,6 @@ void EditorNode::_notification(int p_what) { } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int()); FileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files")); EditorFileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files")); EditorFileDialog::set_default_display_mode((EditorFileDialog::DisplayMode)EDITOR_GET("filesystem/file_dialog/display_mode").operator int()); @@ -745,17 +642,10 @@ void EditorNode::_notification(int p_what) { main_vbox->add_theme_constant_override("separation", gui_base->get_theme_constant(SNAME("top_bar_separation"), SNAME("Editor"))); scene_root_parent->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("Content"), SNAME("EditorStyles"))); bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles"))); - tabbar_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer"))); - - scene_tabs->add_theme_constant_override("icon_max_width", gui_base->get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"))); - scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size()); - main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); } - scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE); - _update_scene_tabs(); - + scene_tabs->update_scene_tabs(); recent_scenes->reset_size(); // Update debugger area. @@ -780,7 +670,6 @@ void EditorNode::_notification(int p_what) { prev_scene->set_icon(gui_base->get_theme_icon(SNAME("PrevScene"), SNAME("EditorIcons"))); distraction_free->set_icon(gui_base->get_theme_icon(SNAME("DistractionFree"), SNAME("EditorIcons"))); - scene_tab_add->set_icon(gui_base->get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); bottom_panel_raise->set_icon(gui_base->get_theme_icon(SNAME("ExpandBottomDock"), SNAME("EditorIcons"))); @@ -1139,14 +1028,13 @@ void EditorNode::_reload_modified_scenes() { } } - set_current_scene(current_idx); - _update_scene_tabs(); + _set_current_scene(current_idx); + scene_tabs->update_scene_tabs(); disk_changed->hide(); } void EditorNode::_reload_project_settings() { ProjectSettings::get_singleton()->setup(ProjectSettings::get_singleton()->get_resource_path(), String(), true); - settings_changed = true; } void EditorNode::_vp_resized() { @@ -1387,6 +1275,10 @@ void EditorNode::_menu_confirm_current() { _menu_option_confirm(current_menu_option, true); } +void EditorNode::trigger_menu_option(int p_option, bool p_confirmed) { + _menu_option_confirm(p_option, p_confirmed); +} + void EditorNode::_dialog_display_save_error(String p_file, Error p_error) { if (p_error) { switch (p_error) { @@ -1822,7 +1714,7 @@ void EditorNode::_save_scene(String p_file, int idx) { editor_folding.save_scene_folding(scene, p_file); _update_title(); - _update_scene_tabs(); + scene_tabs->update_scene_tabs(); } else { _dialog_display_save_error(p_file, err); } @@ -1937,7 +1829,7 @@ void EditorNode::_mark_unsaved_scenes() { } _update_title(); - _update_scene_tabs(); + scene_tabs->update_scene_tabs(); } void EditorNode::_dialog_action(String p_file) { @@ -2145,52 +2037,61 @@ bool EditorNode::_is_class_editor_disabled_by_feature_profile(const StringName & void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) { ERR_FAIL_NULL(p_editing_owner); - if (p_object && _is_class_editor_disabled_by_feature_profile(p_object->get_class())) { + // Editing for this type of object may be disabled by user's feature profile. + if (!p_object || _is_class_editor_disabled_by_feature_profile(p_object->get_class())) { + // Nothing to edit, clean up the owner context and return. + hide_unused_editors(p_editing_owner); return; } - Vector<EditorPlugin *> item_plugins; - if (p_object) { - item_plugins = editor_data.get_subeditors(p_object); + // Get a list of editor plugins that can handle this type of object. + Vector<EditorPlugin *> available_plugins = editor_data.get_handling_sub_editors(p_object); + if (available_plugins.is_empty()) { + // None, clean up the owner context and return. + hide_unused_editors(p_editing_owner); + return; } - if (!item_plugins.is_empty()) { - ObjectID owner_id = p_editing_owner->get_instance_id(); + ObjectID owner_id = p_editing_owner->get_instance_id(); - List<EditorPlugin *> to_remove; - for (EditorPlugin *plugin : active_plugins[owner_id]) { - if (!item_plugins.has(plugin)) { - // Remove plugins no longer used by this editing owner. - to_remove.push_back(plugin); - _plugin_over_edit(plugin, nullptr); - } - } + // Remove editor plugins no longer used by this editing owner. Keep the ones that can + // still be reused by the new edited object. - for (EditorPlugin *plugin : to_remove) { - active_plugins[owner_id].erase(plugin); + List<EditorPlugin *> to_remove; + for (EditorPlugin *plugin : active_plugins[owner_id]) { + if (!available_plugins.has(plugin)) { + to_remove.push_back(plugin); + _plugin_over_edit(plugin, nullptr); } + } - for (EditorPlugin *plugin : item_plugins) { - if (active_plugins[owner_id].has(plugin)) { - plugin->edit(p_object); - continue; - } + for (EditorPlugin *plugin : to_remove) { + active_plugins[owner_id].erase(plugin); + } - for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) { - if (kv.key != owner_id) { - EditorPropertyResource *epres = Object::cast_to<EditorPropertyResource>(ObjectDB::get_instance(kv.key)); - if (epres && kv.value.has(plugin)) { - // If it's resource property editing the same resource type, fold it. - epres->fold_resource(); - } - kv.value.erase(plugin); + // Send the edited object to the plugins. + for (EditorPlugin *plugin : available_plugins) { + if (active_plugins[owner_id].has(plugin)) { + // Plugin was already active, just change the object. + plugin->edit(p_object); + continue; + } + + // If plugin is already associated with another owner, remove it from there first. + for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) { + if (kv.key != owner_id) { + EditorPropertyResource *epres = Object::cast_to<EditorPropertyResource>(ObjectDB::get_instance(kv.key)); + if (epres && kv.value.has(plugin)) { + // If it's resource property editing the same resource type, fold it. + epres->fold_resource(); } + kv.value.erase(plugin); } - active_plugins[owner_id].insert(plugin); - _plugin_over_edit(plugin, p_object); } - } else { - hide_unused_editors(p_editing_owner); + + // Activate previously inactive plugin and edit the object. + active_plugins[owner_id].insert(plugin); + _plugin_over_edit(plugin, p_object); } } @@ -2277,7 +2178,8 @@ void EditorNode::_edit_current(bool p_skip_foreign) { Ref<Resource> res = Object::cast_to<Resource>(current_obj); if (p_skip_foreign && res.is_valid()) { - if (res->get_path().find("::") > -1 && res->get_path().get_slice("::", 0) != editor_data.get_scene_path(get_current_tab())) { + const int current_tab = scene_tabs->get_current_tab(); + if (res->get_path().find("::") > -1 && res->get_path().get_slice("::", 0) != editor_data.get_scene_path(current_tab)) { // Trying to edit resource that belongs to another scene; abort. current_obj = nullptr; } @@ -2291,15 +2193,16 @@ void EditorNode::_edit_current(bool p_skip_foreign) { InspectorDock::get_inspector_singleton()->edit(nullptr); NodeDock::get_singleton()->set_node(nullptr); InspectorDock::get_singleton()->update(nullptr); - hide_unused_editors(); - return; } - Object *prev_inspected_object = InspectorDock::get_inspector_singleton()->get_edited_object(); - + // Update the use folding setting and state. bool disable_folding = bool(EDITOR_GET("interface/inspector/disable_folding")); + if (InspectorDock::get_inspector_singleton()->is_using_folding() == disable_folding) { + InspectorDock::get_inspector_singleton()->set_use_folding(!disable_folding, false); + } + bool is_resource = current_obj->is_class("Resource"); bool is_node = current_obj->is_class("Node"); bool stay_in_script_editor_on_node_selected = bool(EDITOR_GET("text_editor/behavior/navigation/stay_in_script_editor_on_node_selected")); @@ -2317,6 +2220,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) { if (is_resource) { Resource *current_res = Object::cast_to<Resource>(current_obj); ERR_FAIL_COND(!current_res); + InspectorDock::get_inspector_singleton()->edit(current_res); SceneTreeDock::get_singleton()->set_selected(nullptr); NodeDock::get_singleton()->set_node(nullptr); @@ -2406,27 +2310,18 @@ void EditorNode::_edit_current(bool p_skip_foreign) { InspectorDock::get_singleton()->update(nullptr); } - if (current_obj == prev_inspected_object) { - // Make sure inspected properties are restored. - InspectorDock::get_inspector_singleton()->update_tree(); - } - InspectorDock::get_singleton()->set_info( info_is_warning ? TTR("Changes may be lost!") : TTR("This object is read-only."), editable_info, info_is_warning); - if (InspectorDock::get_inspector_singleton()->is_using_folding() == disable_folding) { - InspectorDock::get_inspector_singleton()->set_use_folding(!disable_folding); - } - Object *editor_owner = is_node ? (Object *)SceneTreeDock::get_singleton() : is_resource ? (Object *)InspectorDock::get_inspector_singleton() : (Object *)this; // Take care of the main editor plugin. if (!inspector_only) { - EditorPlugin *main_plugin = editor_data.get_editor(current_obj); + EditorPlugin *main_plugin = editor_data.get_handling_main_editor(current_obj); int plugin_index = 0; for (; plugin_index < editor_table.size(); plugin_index++) { @@ -3110,7 +3005,7 @@ void EditorNode::_discard_changes(const String &p_str) { // Don't close tabs when exiting the editor (required for "restore_scenes_on_load" setting). if (!_is_closing_editor()) { _remove_scene(tab_closing_idx); - _update_scene_tabs(); + scene_tabs->update_scene_tabs(); } _proceed_closing_scene_tabs(); } break; @@ -3448,11 +3343,11 @@ void EditorNode::_remove_edited_scene(bool p_change_tab) { } if (p_change_tab) { - _scene_tab_changed(new_index); + _set_current_scene(new_index); } editor_data.remove_scene(old_index); _update_title(); - _update_scene_tabs(); + scene_tabs->update_scene_tabs(); } void EditorNode::_remove_scene(int index, bool p_change_tab) { @@ -3574,7 +3469,15 @@ bool EditorNode::is_changing_scene() const { return changing_scene; } -void EditorNode::set_current_scene(int p_idx) { +void EditorNode::_set_current_scene(int p_idx) { + if (p_idx == editor_data.get_edited_scene()) { + return; // Pointless. + } + + _set_current_scene_nocheck(p_idx); +} + +void EditorNode::_set_current_scene_nocheck(int p_idx) { // Save the folding in case the scene gets reloaded. if (editor_data.get_scene_path(p_idx) != "" && editor_data.get_edited_scene_root(p_idx)) { editor_folding.save_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx)); @@ -3621,7 +3524,7 @@ void EditorNode::set_current_scene(int p_idx) { _edit_current(true); _update_title(); - _update_scene_tabs(); + scene_tabs->update_scene_tabs(); if (tabs_to_close.is_empty()) { call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up. @@ -3664,9 +3567,9 @@ int EditorNode::new_scene() { } idx = MAX(idx, 0); - _scene_tab_changed(idx); + _set_current_scene(idx); editor_data.clear_editor_states(); - _update_scene_tabs(); + scene_tabs->update_scene_tabs(); return idx; } @@ -3679,7 +3582,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b if (!p_set_inherited) { for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { if (editor_data.get_scene_path(i) == p_scene) { - _scene_tab_changed(i); + _set_current_scene(i); return OK; } } @@ -3710,10 +3613,10 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b if (!editor_data.get_edited_scene_root() && editor_data.get_edited_scene_count() == 2) { _remove_edited_scene(); - } else if (!p_silent_change_tab) { - _scene_tab_changed(idx); + } else if (p_silent_change_tab) { + _set_current_scene_nocheck(idx); } else { - set_current_scene(idx); + _set_current_scene(idx); } dependency_errors.clear(); @@ -3725,7 +3628,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b opening_prev = false; if (prev != -1) { - set_current_scene(prev); + _set_current_scene(prev); editor_data.remove_scene(idx); } return ERR_FILE_NOT_FOUND; @@ -3741,7 +3644,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b opening_prev = false; if (prev != -1) { - set_current_scene(prev); + _set_current_scene(prev); editor_data.remove_scene(idx); } return ERR_FILE_MISSING_DEPENDENCIES; @@ -3777,7 +3680,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b _dialog_display_load_error(lpath, ERR_FILE_CORRUPT); opening_prev = false; if (prev != -1) { - set_current_scene(prev); + _set_current_scene(prev); editor_data.remove_scene(idx); } return ERR_FILE_CORRUPT; @@ -3803,7 +3706,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b } _update_title(); - _update_scene_tabs(); + scene_tabs->update_scene_tabs(); _add_to_recent_scenes(lpath); if (editor_folding.has_folding_data(lpath)) { @@ -3815,11 +3718,14 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b prev_scene->set_disabled(previous_scenes.size() == 0); opening_prev = false; - SceneTreeDock::get_singleton()->set_selected(new_scene); EditorDebuggerNode::get_singleton()->update_live_edit_root(); - push_item(new_scene); + // Tell everything to edit this object, unless we're in the process of restoring scenes. + // If we are, we'll edit it after the restoration is done. + if (!restoring_scenes) { + push_item(new_scene); + } // Load the selected nodes. if (editor_state_cf->has_section_key("editor_states", "selected_nodes")) { @@ -3998,6 +3904,10 @@ void EditorNode::open_request(const String &p_path) { load_scene(p_path); // As it will be opened in separate tab. } +bool EditorNode::has_previous_scenes() const { + return !previous_scenes.is_empty(); +} + void EditorNode::edit_foreign_resource(Ref<Resource> p_resource) { load_scene(p_resource->get_path().get_slice("::", 0)); InspectorDock::get_singleton()->call_deferred("edit_resource", p_resource); @@ -5408,7 +5318,7 @@ void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout) { String current_scene = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "current_scene"); int current_scene_idx = scenes.find(current_scene); if (current_scene_idx >= 0) { - set_current_scene(current_scene_idx); + _set_current_scene(current_scene_idx); } } @@ -5500,14 +5410,6 @@ void EditorNode::cleanup() { _init_callbacks.clear(); } -int EditorNode::get_current_tab() { - return scene_tabs->get_current_tab(); -} - -void EditorNode::set_current_tab(int p_tab) { - scene_tabs->set_current_tab(p_tab); -} - void EditorNode::_update_layouts_menu() { editor_layouts->clear(); overridden_default_layout = -1; @@ -5572,13 +5474,6 @@ void EditorNode::_layout_menu_option(int p_id) { } } -void EditorNode::_scene_tab_script_edited(int p_tab) { - Ref<Script> scr = editor_data.get_scene_root_script(p_tab); - if (scr.is_valid()) { - InspectorDock::get_singleton()->edit_resource(scr); - } -} - void EditorNode::_proceed_closing_scene_tabs() { List<String>::Element *E = tabs_to_close.front(); if (!E) { @@ -5610,8 +5505,8 @@ bool EditorNode::_is_closing_editor() const { return tab_closing_menu_option == FILE_QUIT || tab_closing_menu_option == RUN_PROJECT_MANAGER || tab_closing_menu_option == RELOAD_CURRENT_PROJECT; } -void EditorNode::_scene_tab_closed(int p_tab, int p_option) { - current_menu_option = p_option; +void EditorNode::_scene_tab_closed(int p_tab) { + current_menu_option = SCENE_TAB_CLOSE; tab_closing_idx = p_tab; Node *scene = editor_data.get_edited_scene_root(p_tab); if (!scene) { @@ -5639,17 +5534,12 @@ void EditorNode::_scene_tab_closed(int p_tab, int p_option) { } if (!unsaved_message.is_empty()) { - if (get_current_tab() != p_tab) { - set_current_scene(p_tab); + if (scene_tabs->get_current_tab() != p_tab) { + _set_current_scene(p_tab); } - if (current_menu_option == RELOAD_CURRENT_PROJECT) { - save_confirmation->set_ok_button_text(TTR("Save & Reload")); - save_confirmation->set_text(unsaved_message + "\n\n" + TTR("Save before reloading?")); - } else { - save_confirmation->set_ok_button_text(TTR("Save & Close")); - save_confirmation->set_text(unsaved_message + "\n\n" + TTR("Save before closing?")); - } + save_confirmation->set_ok_button_text(TTR("Save & Close")); + save_confirmation->set_text(unsaved_message + "\n\n" + TTR("Save before closing?")); save_confirmation->reset_size(); save_confirmation->popup_centered(); } else { @@ -5657,113 +5547,7 @@ void EditorNode::_scene_tab_closed(int p_tab, int p_option) { } save_editor_layout_delayed(); - _update_scene_tabs(); -} - -void EditorNode::_scene_tab_hovered(int p_tab) { - if (!bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover"))) { - return; - } - int current_tab = scene_tabs->get_current_tab(); - - if (p_tab == current_tab || p_tab < 0) { - tab_preview_panel->hide(); - } else { - String path = editor_data.get_scene_path(p_tab); - if (!path.is_empty()) { - EditorResourcePreview::get_singleton()->queue_resource_preview(path, this, "_thumbnail_done", p_tab); - } - } -} - -void EditorNode::_scene_tab_exit() { - tab_preview_panel->hide(); -} - -void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { - int tab_id = scene_tabs->get_hovered_tab(); - Ref<InputEventMouseButton> mb = p_input; - - if (mb.is_valid()) { - if (tab_id >= 0) { - if (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed()) { - _scene_tab_closed(tab_id); - } - } else if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) { - int tab_buttons = 0; - if (scene_tabs->get_offset_buttons_visible()) { - tab_buttons = theme->get_icon(SNAME("increment"), SNAME("TabBar"))->get_width() + theme->get_icon(SNAME("decrement"), SNAME("TabBar"))->get_width(); - } - - if ((gui_base->is_layout_rtl() && mb->get_position().x > tab_buttons) || (!gui_base->is_layout_rtl() && mb->get_position().x < scene_tabs->get_size().width - tab_buttons)) { - _menu_option_confirm(FILE_NEW_SCENE, true); - } - } - - if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { - // Context menu. - scene_tabs_context_menu->clear(); - scene_tabs_context_menu->reset_size(); - - scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), FILE_NEW_SCENE); - if (tab_id >= 0) { - scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), FILE_SAVE_SCENE); - scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), FILE_SAVE_AS_SCENE); - } - scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES); - if (tab_id >= 0) { - scene_tabs_context_menu->add_separator(); - scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM); - scene_tabs_context_menu->add_item(TTR("Play This Scene"), FILE_RUN_SCENE); - - scene_tabs_context_menu->add_separator(); - scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/close_scene"), FILE_CLOSE); - scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(FILE_CLOSE), TTR("Close Tab")); - scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/reopen_closed_scene"), FILE_OPEN_PREV); - scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(FILE_OPEN_PREV), TTR("Undo Close Tab")); - if (previous_scenes.is_empty()) { - scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(FILE_OPEN_PREV), true); - } - scene_tabs_context_menu->add_item(TTR("Close Other Tabs"), FILE_CLOSE_OTHERS); - if (editor_data.get_edited_scene_count() <= 1) { - scene_tabs_context_menu->set_item_disabled(file_menu->get_item_index(FILE_CLOSE_OTHERS), true); - } - scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right"), FILE_CLOSE_RIGHT); - if (editor_data.get_edited_scene_count() == tab_id + 1) { - scene_tabs_context_menu->set_item_disabled(file_menu->get_item_index(FILE_CLOSE_RIGHT), true); - } - scene_tabs_context_menu->add_item(TTR("Close All Tabs"), FILE_CLOSE_ALL); - } - scene_tabs_context_menu->set_position(scene_tabs->get_screen_position() + mb->get_position()); - scene_tabs_context_menu->reset_size(); - scene_tabs_context_menu->popup(); - } - } -} - -void EditorNode::_reposition_active_tab(int idx_to) { - editor_data.move_edited_scene_to_index(idx_to); - _update_scene_tabs(); -} - -void EditorNode::_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata) { - int p_tab = p_udata.operator signed int(); - if (p_preview.is_valid()) { - Rect2 rect = scene_tabs->get_tab_rect(p_tab); - rect.position += scene_tabs->get_global_position(); - tab_preview->set_texture(p_preview); - tab_preview_panel->set_position(rect.position + Vector2(0, rect.size.height)); - tab_preview_panel->show(); - } -} - -void EditorNode::_scene_tab_changed(int p_tab) { - tab_preview_panel->hide(); - - if (p_tab == editor_data.get_edited_scene()) { - return; // Pointless. - } - set_current_scene(p_tab); + scene_tabs->update_scene_tabs(); } Button *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) { @@ -6078,19 +5862,6 @@ PopupMenu *EditorNode::get_export_as_menu() { return export_as_menu; } -void EditorNode::_global_menu_scene(const Variant &p_tag) { - int idx = (int)p_tag; - scene_tabs->set_current_tab(idx); -} - -void EditorNode::_global_menu_new_window(const Variant &p_tag) { - if (OS::get_singleton()->get_main_loop()) { - List<String> args; - args.push_back("-p"); - OS::get_singleton()->create_instance(args); - } -} - void EditorNode::_dropped_files(const Vector<String> &p_files) { String to_path = ProjectSettings::get_singleton()->globalize_path(FileSystemDock::get_singleton()->get_current_directory()); @@ -6708,9 +6479,6 @@ void EditorNode::_feature_profile_changed() { } void EditorNode::_bind_methods() { - ClassDB::bind_method("edit_current", &EditorNode::edit_current); - ClassDB::bind_method("edit_node", &EditorNode::edit_node); - ClassDB::bind_method(D_METHOD("push_item", "object", "property", "inspector_only"), &EditorNode::push_item, DEFVAL(""), DEFVAL(false)); ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene); @@ -6720,18 +6488,13 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process); - ClassDB::bind_method("set_current_scene", &EditorNode::set_current_scene); - ClassDB::bind_method("_thumbnail_done", &EditorNode::_thumbnail_done); ClassDB::bind_method("_set_main_scene_state", &EditorNode::_set_main_scene_state); ClassDB::bind_method("_update_recent_scenes", &EditorNode::_update_recent_scenes); - ClassDB::bind_method(D_METHOD("get_gui_base"), &EditorNode::get_gui_base); - ADD_SIGNAL(MethodInfo("request_help_search")); ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "args"))); ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj"))); ADD_SIGNAL(MethodInfo("scene_saved", PropertyInfo(Variant::STRING, "path"))); - ADD_SIGNAL(MethodInfo("project_settings_changed")); ADD_SIGNAL(MethodInfo("scene_changed")); ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "path"))); } @@ -6817,10 +6580,6 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p return eta.exitcode; } -void EditorNode::notify_settings_changed() { - settings_changed = true; -} - EditorNode::EditorNode() { EditorPropertyNameProcessor *epnp = memnew(EditorPropertyNameProcessor); add_child(epnp); @@ -6868,6 +6627,7 @@ EditorNode::EditorNode() { EditorUndoRedoManager::get_singleton()->connect("version_changed", callable_mp(this, &EditorNode::_update_undo_redo_allowed)); EditorUndoRedoManager::get_singleton()->connect("history_changed", callable_mp(this, &EditorNode::_update_undo_redo_allowed)); + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorNode::_update_from_settings)); TranslationServer::get_singleton()->set_enabled(false); // Load settings. @@ -7268,62 +7028,13 @@ EditorNode::EditorNode() { VBoxContainer *srt = memnew(VBoxContainer); srt->set_v_size_flags(Control::SIZE_EXPAND_FILL); - top_split->add_child(srt); srt->add_theme_constant_override("separation", 0); + top_split->add_child(srt); - tab_preview_panel = memnew(Panel); - tab_preview_panel->set_size(Size2(100, 100) * EDSCALE); - tab_preview_panel->hide(); - tab_preview_panel->set_self_modulate(Color(1, 1, 1, 0.7)); - gui_base->add_child(tab_preview_panel); - - tab_preview = memnew(TextureRect); - tab_preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); - tab_preview->set_size(Size2(96, 96) * EDSCALE); - tab_preview->set_position(Point2(2, 2) * EDSCALE); - tab_preview_panel->add_child(tab_preview); - - tabbar_panel = memnew(PanelContainer); - tabbar_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer"))); - srt->add_child(tabbar_panel); - tabbar_container = memnew(HBoxContainer); - tabbar_panel->add_child(tabbar_container); - - scene_tabs = memnew(TabBar); - scene_tabs->set_select_with_rmb(true); - scene_tabs->add_tab("unsaved"); - scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int()); - scene_tabs->add_theme_constant_override("icon_max_width", gui_base->get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"))); - scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE); - scene_tabs->set_drag_to_rearrange_enabled(true); - scene_tabs->set_auto_translate(false); - scene_tabs->connect("tab_changed", callable_mp(this, &EditorNode::_scene_tab_changed)); - scene_tabs->connect("tab_button_pressed", callable_mp(this, &EditorNode::_scene_tab_script_edited)); - scene_tabs->connect("tab_close_pressed", callable_mp(this, &EditorNode::_scene_tab_closed).bind(SCENE_TAB_CLOSE)); - scene_tabs->connect("tab_hovered", callable_mp(this, &EditorNode::_scene_tab_hovered)); - scene_tabs->connect("mouse_exited", callable_mp(this, &EditorNode::_scene_tab_exit)); - scene_tabs->connect("gui_input", callable_mp(this, &EditorNode::_scene_tab_input)); - scene_tabs->connect("active_tab_rearranged", callable_mp(this, &EditorNode::_reposition_active_tab)); - scene_tabs->connect("resized", callable_mp(this, &EditorNode::_update_scene_tabs)); - scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL); - tabbar_container->add_child(scene_tabs); - - scene_tabs_context_menu = memnew(PopupMenu); - tabbar_container->add_child(scene_tabs_context_menu); - scene_tabs_context_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); - - scene_tab_add = memnew(Button); - scene_tab_add->set_flat(true); - scene_tab_add->set_tooltip_text(TTR("Add a new scene.")); - scene_tab_add->set_icon(gui_base->get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); - scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f)); - scene_tabs->add_child(scene_tab_add); - scene_tab_add->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(FILE_NEW_SCENE)); - - scene_tab_add_ph = memnew(Control); - scene_tab_add_ph->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); - scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size()); - tabbar_container->add_child(scene_tab_add_ph); + scene_tabs = memnew(EditorSceneTabs); + srt->add_child(scene_tabs); + scene_tabs->connect("tab_changed", callable_mp(this, &EditorNode::_set_current_scene)); + scene_tabs->connect("tab_closed", callable_mp(this, &EditorNode::_scene_tab_closed)); distraction_free = memnew(Button); distraction_free->set_flat(true); @@ -7331,10 +7042,10 @@ EditorNode::EditorNode() { ED_SHORTCUT_OVERRIDE("editor/distraction_free_mode", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::D); distraction_free->set_shortcut(ED_GET_SHORTCUT("editor/distraction_free_mode")); distraction_free->set_tooltip_text(TTR("Toggle distraction-free mode.")); - distraction_free->connect("pressed", callable_mp(this, &EditorNode::_toggle_distraction_free_mode)); distraction_free->set_icon(gui_base->get_theme_icon(SNAME("DistractionFree"), SNAME("EditorIcons"))); distraction_free->set_toggle_mode(true); - tabbar_container->add_child(distraction_free); + scene_tabs->add_extra_button(distraction_free); + distraction_free->connect("pressed", callable_mp(this, &EditorNode::_toggle_distraction_free_mode)); scene_root_parent = memnew(PanelContainer); scene_root_parent->set_custom_minimum_size(Size2(0, 80) * EDSCALE); @@ -8157,7 +7868,7 @@ EditorNode::EditorNode() { editor_data.add_edited_scene(-1); editor_data.set_edited_scene(0); - _update_scene_tabs(); + scene_tabs->update_scene_tabs(); ImportDock::get_singleton()->initialize_import_options(); diff --git a/editor/editor_node.h b/editor/editor_node.h index 86b4847e5b..d39e848de8 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -93,6 +93,7 @@ class EditorResourcePreview; class EditorResourceConversionPlugin; class EditorRunBar; class EditorRunNative; +class EditorSceneTabs; class EditorSelectionHistory; class EditorSettingsDialog; class EditorTitleBar; @@ -154,6 +155,8 @@ public: }; private: + friend class EditorSceneTabs; + enum MenuOptions { FILE_NEW_SCENE, FILE_NEW_INHERITED_SCENE, @@ -216,6 +219,7 @@ private: SETTINGS_PICK_MAIN_SCENE, SETTINGS_TOGGLE_FULLSCREEN, SETTINGS_HELP, + SCENE_TAB_CLOSE, EDITOR_SCREENSHOT, @@ -235,9 +239,6 @@ private: SET_RENDERER_NAME_SAVE_AND_RESTART, - GLOBAL_NEW_WINDOW, - GLOBAL_SCENE, - IMPORT_PLUGIN_BASE = 100, TOOL_MENU_BASE = 1000 @@ -319,10 +320,7 @@ private: Vector<HSplitContainer *> hsplits; // Main tabs. - TabBar *scene_tabs = nullptr; - PopupMenu *scene_tabs_context_menu = nullptr; - Panel *tab_preview_panel = nullptr; - TextureRect *tab_preview = nullptr; + EditorSceneTabs *scene_tabs = nullptr; int tab_closing_idx = 0; List<String> tabs_to_close; @@ -437,11 +435,7 @@ private: int dock_popup_selected_idx = -1; int dock_select_rect_over_idx = -1; - PanelContainer *tabbar_panel = nullptr; - HBoxContainer *tabbar_container = nullptr; Button *distraction_free = nullptr; - Button *scene_tab_add = nullptr; - Control *scene_tab_add_ph = nullptr; Vector<BottomPanelItem> bottom_panel_items; PanelContainer *bottom_panel = nullptr; @@ -465,7 +459,6 @@ private: bool immediate_dialog_confirmed = false; bool opening_prev = false; bool restoring_scenes = false; - bool settings_changed = true; // Make it update settings on first frame. bool unsaved_cache = true; bool waiting_for_first_scan = true; @@ -567,7 +560,6 @@ private: void _save_editor_states(const String &p_file, int p_idx = -1); void _load_editor_plugin_states_from_config(const Ref<ConfigFile> &p_config_file); void _update_title(); - void _update_scene_tabs(); void _version_control_menu_option(int p_idx); void _close_messages(); void _show_messages(); @@ -579,11 +571,14 @@ private: int _save_external_resources(); + void _set_current_scene(int p_idx); + void _set_current_scene_nocheck(int p_idx); bool _validate_scene_recursive(const String &p_filename, Node *p_node); void _save_scene(String p_file, int idx = -1); void _save_all_scenes(); int _next_unsaved_scene(bool p_valid_filename, int p_start = 0); void _discard_changes(const String &p_str = String()); + void _scene_tab_closed(int p_tab); void _inherit_request(String p_file); void _instantiate_request(const Vector<String> &p_files); @@ -597,8 +592,6 @@ private: void _add_to_recent_scenes(const String &p_scene); void _update_recent_scenes(); void _open_recent_scene(int p_idx); - void _global_menu_scene(const Variant &p_tag); - void _global_menu_new_window(const Variant &p_tag); void _dropped_files(const Vector<String> &p_files); void _add_dropped_files_recursive(const Vector<String> &p_files, String to_path); @@ -637,16 +630,9 @@ private: void _dock_floating_close_request(WindowWrapper *p_wrapper); void _dock_make_selected_float(); void _dock_make_float(Control *p_control, int p_slot_index, bool p_show_window = true); - void _scene_tab_changed(int p_tab); + void _proceed_closing_scene_tabs(); bool _is_closing_editor() const; - void _scene_tab_closed(int p_tab, int p_option = SCENE_TAB_CLOSE); - void _scene_tab_hovered(int p_tab); - void _scene_tab_exit(); - void _scene_tab_input(const Ref<InputEvent> &p_input); - void _reposition_active_tab(int idx_to); - void _thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata); - void _scene_tab_script_edited(int p_tab); Dictionary _get_main_scene_state(); void _set_main_scene_state(Dictionary p_state, Node *p_for_scene); @@ -706,9 +692,6 @@ protected: static void _bind_methods(); void _notification(int p_what); - int get_current_tab(); - void set_current_tab(int p_tab); - public: // Public for use with callable_mp. void _on_plugin_ready(Object *p_script, const String &p_activate_name); @@ -765,6 +748,9 @@ public: ProjectSettingsEditor *get_project_settings() { return project_settings_editor; } + void trigger_menu_option(int p_option, bool p_confirmed); + bool has_previous_scenes() const; + void new_inherited_scene() { _menu_option_confirm(FILE_NEW_INHERITED_SCENE, false); } void set_docks_visible(bool p_show); @@ -850,8 +836,6 @@ public: bool is_scene_open(const String &p_path); - void set_current_scene(int p_idx); - void setup_color_picker(ColorPicker *p_picker); void request_instantiate_scene(const String &p_path); @@ -926,8 +910,6 @@ public: void try_autosave(); void restart_editor(); - void notify_settings_changed(); - void dim_editor(bool p_dimming); bool is_editor_dimmed() const; diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 2d4c07b263..2726a781b4 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -378,11 +378,11 @@ bool EditorPlugin::get_remove_list(List<Node *> *p_list) { } void EditorPlugin::add_undo_redo_inspector_hook_callback(Callable p_callable) { - EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(p_callable); + EditorNode::get_editor_data().add_undo_redo_inspector_hook_callback(p_callable); } void EditorPlugin::remove_undo_redo_inspector_hook_callback(Callable p_callable) { - EditorNode::get_singleton()->get_editor_data().remove_undo_redo_inspector_hook_callback(p_callable); + EditorNode::get_editor_data().remove_undo_redo_inspector_hook_callback(p_callable); } void EditorPlugin::add_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser) { @@ -527,19 +527,24 @@ void EditorPlugin::remove_resource_conversion_plugin(const Ref<EditorResourceCon EditorNode::get_singleton()->remove_resource_conversion_plugin(p_plugin); } +#ifndef DISABLE_DEPRECATED void EditorPlugin::_editor_project_settings_changed() { emit_signal(SNAME("project_settings_changed")); } +#endif + void EditorPlugin::_notification(int p_what) { +#ifndef DISABLE_DEPRECATED switch (p_what) { case NOTIFICATION_ENTER_TREE: { - EditorNode::get_singleton()->connect("project_settings_changed", callable_mp(this, &EditorPlugin::_editor_project_settings_changed)); + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorPlugin::_editor_project_settings_changed)); } break; case NOTIFICATION_EXIT_TREE: { - EditorNode::get_singleton()->disconnect("project_settings_changed", callable_mp(this, &EditorPlugin::_editor_project_settings_changed)); + ProjectSettings::get_singleton()->disconnect("settings_changed", callable_mp(this, &EditorPlugin::_editor_project_settings_changed)); } break; } +#endif } void EditorPlugin::_bind_methods() { diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 7dcf62144d..62ed432ecc 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -63,7 +63,9 @@ class EditorPlugin : public Node { String last_main_screen_name; String plugin_version; +#ifndef DISABLE_DEPRECATED void _editor_project_settings_changed(); +#endif protected: void _notification(int p_what); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index ee020223e6..92789275a9 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1299,7 +1299,7 @@ EditorPropertyLayers::EditorPropertyLayers() { layers->set_hide_on_checkable_item_selection(false); layers->connect("id_pressed", callable_mp(this, &EditorPropertyLayers::_menu_pressed)); layers->connect("popup_hide", callable_mp((BaseButton *)button, &BaseButton::set_pressed).bind(false)); - EditorNode::get_singleton()->connect("project_settings_changed", callable_mp(this, &EditorPropertyLayers::_refresh_names)); + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorPropertyLayers::_refresh_names)); } ///////////////////// INT ///////////////////////// diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 5a490fcfc0..b923566bfa 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -513,6 +513,12 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "filesystem/file_dialog/display_mode", 0, "Thumbnails,List") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "filesystem/file_dialog/thumbnail_size", 64, "32,128,16") + // Import (for glft module) + EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/import/blender/blender3_path", "", "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "filesystem/import/blender/rpc_port", 6011, "0,65535,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "filesystem/import/blender/rpc_server_uptime", 5, "0,300,1,or_greater,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/import/fbx/fbx2gltf_path", "", "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + /* Docks */ // SceneTree diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 6087ea3abc..b8a112859e 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1507,6 +1507,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_theme_preview_bg_tab->set_expand_margin(SIDE_BOTTOM, 2 * EDSCALE); theme->set_stylebox("ThemeEditorPreviewBG", "EditorStyles", style_theme_preview_bg_tab); + Ref<StyleBoxFlat> style_texture_region_bg = style_tree_bg->duplicate(); + style_texture_region_bg->set_content_margin_all(0); + theme->set_stylebox("TextureRegionPreviewBG", "EditorStyles", style_texture_region_bg); + theme->set_stylebox("TextureRegionPreviewFG", "EditorStyles", make_empty_stylebox(0, 0, 0, 0)); + // Separators theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width))); theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width), 0, 0, true)); diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp index abfbd5e7c0..2b059352a7 100644 --- a/editor/editor_undo_redo_manager.cpp +++ b/editor/editor_undo_redo_manager.cpp @@ -70,7 +70,7 @@ int EditorUndoRedoManager::get_history_id_for_object(Object *p_object) const { Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); if (edited_scene && (node == edited_scene || edited_scene->is_ancestor_of(node))) { - int idx = EditorNode::get_singleton()->get_editor_data().get_current_edited_scene_history_id(); + int idx = EditorNode::get_editor_data().get_current_edited_scene_history_id(); if (idx > 0) { history_id = idx; } @@ -80,12 +80,12 @@ int EditorUndoRedoManager::get_history_id_for_object(Object *p_object) const { if (Resource *res = Object::cast_to<Resource>(p_object)) { if (res->is_built_in()) { if (res->get_path().is_empty()) { - int idx = EditorNode::get_singleton()->get_editor_data().get_current_edited_scene_history_id(); + int idx = EditorNode::get_editor_data().get_current_edited_scene_history_id(); if (idx > 0) { history_id = idx; } } else { - int idx = EditorNode::get_singleton()->get_editor_data().get_scene_history_id_from_path(res->get_path().get_slice("::", 0)); + int idx = EditorNode::get_editor_data().get_scene_history_id_from_path(res->get_path().get_slice("::", 0)); if (idx > 0) { history_id = idx; } diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 5ee9b187a2..1ea5fbc759 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -141,7 +141,9 @@ void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags) String host = EDITOR_GET("network/debug/remote_host"); int remote_port = (int)EDITOR_GET("network/debug/remote_port"); - if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) { + if (EditorSettings::get_singleton()->has_setting("export/android/use_wifi_for_remote_debug") && EDITOR_GET("export/android/use_wifi_for_remote_debug")) { + host = EDITOR_GET("export/android/wifi_remote_debug_host"); + } else if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) { host = "localhost"; } @@ -498,7 +500,7 @@ EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_pla //initial export plugin callback for (int i = 0; i < export_plugins.size(); i++) { export_plugins.write[i]->set_export_preset(p_preset); - if (export_plugins[i]->get_script_instance()) { //script based + if (GDVIRTUAL_IS_OVERRIDDEN_PTR(export_plugins[i], _export_begin)) { PackedStringArray features_psa; for (const String &feature : features) { features_psa.push_back(feature); @@ -513,7 +515,7 @@ EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_pla EditorExportPlatform::ExportNotifier::~ExportNotifier() { Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); for (int i = 0; i < export_plugins.size(); i++) { - if (export_plugins[i]->get_script_instance()) { + if (GDVIRTUAL_IS_OVERRIDDEN_PTR(export_plugins[i], _export_end)) { export_plugins.write[i]->_export_end_script(); } export_plugins.write[i]->_export_end(); @@ -1223,7 +1225,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & bool do_export = true; for (int i = 0; i < export_plugins.size(); i++) { - if (export_plugins[i]->get_script_instance()) { //script based + if (GDVIRTUAL_IS_OVERRIDDEN_PTR(export_plugins[i], _export_file)) { export_plugins.write[i]->_export_file_script(path, type, features_psa); } else { export_plugins.write[i]->_export_file(path, type, features); diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 61f875713f..f723a20368 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -65,7 +65,6 @@ void ProjectExportTextureFormatError::_bind_methods() { void ProjectExportTextureFormatError::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { texture_format_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); } break; @@ -1071,7 +1070,7 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) { current->set_export_path(p_path); platform->clear_messages(); - Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0); + Error err = platform->export_project(current, export_debug->is_pressed(), current->get_export_path(), 0); result_dialog_log->clear(); if (err != ERR_SKIP) { if (platform->fill_log_messages(result_dialog_log, err)) { diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 751f1c575d..74345a6bcc 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -45,6 +45,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/gui/editor_dir_dialog.h" +#include "editor/gui/editor_scene_tabs.h" #include "editor/import/resource_importer_scene.h" #include "editor/import_dock.h" #include "editor/plugins/editor_resource_tooltip_plugins.h" @@ -1361,7 +1362,7 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ for (int i = 0; i < file_changed_paths.size(); ++i) { String new_item_path = p_item.is_file ? new_path : file_changed_paths[i].replace_first(old_path, new_path); if (ResourceLoader::get_resource_type(new_item_path) == "PackedScene" && EditorNode::get_singleton()->is_scene_open(file_changed_paths[i])) { - EditorData *ed = &EditorNode::get_singleton()->get_editor_data(); + EditorData *ed = &EditorNode::get_editor_data(); for (int j = 0; j < ed->get_edited_scene_count(); j++) { if (ed->get_scene_path(j) == file_changed_paths[i]) { ed->get_edited_scene_root(j)->set_scene_file_path(new_item_path); @@ -1629,7 +1630,7 @@ void FileSystemDock::_make_scene_confirm() { const String scene_path = make_scene_dialog->get_scene_path(); int idx = EditorNode::get_singleton()->new_scene(); - EditorNode::get_singleton()->get_editor_data().set_scene_path(idx, scene_path); + EditorNode::get_editor_data().set_scene_path(idx, scene_path); EditorNode::get_singleton()->set_edited_scene(make_scene_dialog->create_scene_root()); EditorNode::get_singleton()->save_scene_list({ scene_path }); } @@ -1732,14 +1733,14 @@ void FileSystemDock::_rename_operation_confirm() { HashMap<String, String> folder_renames; _try_move_item(to_rename, new_path, file_renames, folder_renames); - int current_tab = EditorNode::get_singleton()->get_current_tab(); + int current_tab = EditorSceneTabs::get_singleton()->get_current_tab(); _save_scenes_after_move(file_renames); // save scenes before updating _update_dependencies_after_move(file_renames); _update_resource_paths_after_move(file_renames); _update_project_settings_after_move(file_renames); _update_favorites_list_after_move(file_renames, folder_renames); - EditorNode::get_singleton()->set_current_tab(current_tab); + EditorSceneTabs::get_singleton()->set_current_tab(current_tab); print_verbose("FileSystem: calling rescan."); _rescan(); @@ -1881,14 +1882,14 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_cop } if (is_moved) { - int current_tab = EditorNode::get_singleton()->get_current_tab(); + int current_tab = EditorSceneTabs::get_singleton()->get_current_tab(); _save_scenes_after_move(file_renames); // Save scenes before updating. _update_dependencies_after_move(file_renames); _update_resource_paths_after_move(file_renames); _update_project_settings_after_move(file_renames); _update_favorites_list_after_move(file_renames, folder_renames); - EditorNode::get_singleton()->set_current_tab(current_tab); + EditorSceneTabs::get_singleton()->set_current_tab(current_tab); print_verbose("FileSystem: calling rescan."); _rescan(); diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp new file mode 100644 index 0000000000..3732b922c7 --- /dev/null +++ b/editor/gui/editor_scene_tabs.cpp @@ -0,0 +1,384 @@ +/**************************************************************************/ +/* editor_scene_tabs.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "editor_scene_tabs.h" + +#include "editor/editor_node.h" +#include "editor/editor_resource_preview.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" +#include "editor/inspector_dock.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "scene/gui/panel.h" +#include "scene/gui/panel_container.h" +#include "scene/gui/popup_menu.h" +#include "scene/gui/tab_bar.h" +#include "scene/gui/texture_rect.h" + +EditorSceneTabs *EditorSceneTabs::singleton = nullptr; + +void EditorSceneTabs::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + tabbar_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer"))); + scene_tabs->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"))); + + scene_tab_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f)); + + scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size()); + } break; + + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int()); + scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE); + } break; + } +} + +void EditorSceneTabs::_scene_tab_changed(int p_tab) { + tab_preview_panel->hide(); + + emit_signal("tab_changed", p_tab); +} + +void EditorSceneTabs::_scene_tab_script_edited(int p_tab) { + Ref<Script> scr = EditorNode::get_editor_data().get_scene_root_script(p_tab); + if (scr.is_valid()) { + InspectorDock::get_singleton()->edit_resource(scr); + } +} + +void EditorSceneTabs::_scene_tab_closed(int p_tab) { + emit_signal("tab_closed", p_tab); +} + +void EditorSceneTabs::_scene_tab_hovered(int p_tab) { + if (!bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover"))) { + return; + } + int current_tab = scene_tabs->get_current_tab(); + + if (p_tab == current_tab || p_tab < 0) { + tab_preview_panel->hide(); + } else { + String path = EditorNode::get_editor_data().get_scene_path(p_tab); + if (!path.is_empty()) { + EditorResourcePreview::get_singleton()->queue_resource_preview(path, this, "_tab_preview_done", p_tab); + } + } +} + +void EditorSceneTabs::_scene_tab_exit() { + tab_preview_panel->hide(); +} + +void EditorSceneTabs::_scene_tab_input(const Ref<InputEvent> &p_input) { + int tab_id = scene_tabs->get_hovered_tab(); + Ref<InputEventMouseButton> mb = p_input; + + if (mb.is_valid()) { + if (tab_id >= 0) { + if (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed()) { + _scene_tab_closed(tab_id); + } + } else if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) { + int tab_buttons = 0; + if (scene_tabs->get_offset_buttons_visible()) { + tab_buttons = get_theme_icon(SNAME("increment"), SNAME("TabBar"))->get_width() + get_theme_icon(SNAME("decrement"), SNAME("TabBar"))->get_width(); + } + + if ((is_layout_rtl() && mb->get_position().x > tab_buttons) || (!is_layout_rtl() && mb->get_position().x < scene_tabs->get_size().width - tab_buttons)) { + EditorNode::get_singleton()->trigger_menu_option(EditorNode::FILE_NEW_SCENE, true); + } + } + if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { + // Context menu. + _update_context_menu(); + + scene_tabs_context_menu->set_position(scene_tabs->get_screen_position() + mb->get_position()); + scene_tabs_context_menu->reset_size(); + scene_tabs_context_menu->popup(); + } + } +} + +void EditorSceneTabs::_reposition_active_tab(int p_to_index) { + EditorNode::get_editor_data().move_edited_scene_to_index(p_to_index); + update_scene_tabs(); +} + +void EditorSceneTabs::_update_context_menu() { + scene_tabs_context_menu->clear(); + scene_tabs_context_menu->reset_size(); + + int tab_id = scene_tabs->get_hovered_tab(); + + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), EditorNode::FILE_NEW_SCENE); + if (tab_id >= 0) { + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), EditorNode::FILE_SAVE_SCENE); + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), EditorNode::FILE_SAVE_AS_SCENE); + } + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), EditorNode::FILE_SAVE_ALL_SCENES); + if (tab_id >= 0) { + scene_tabs_context_menu->add_separator(); + scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), EditorNode::FILE_SHOW_IN_FILESYSTEM); + scene_tabs_context_menu->add_item(TTR("Play This Scene"), EditorNode::FILE_RUN_SCENE); + + scene_tabs_context_menu->add_separator(); + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/close_scene"), EditorNode::FILE_CLOSE); + scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(EditorNode::FILE_CLOSE), TTR("Close Tab")); + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/reopen_closed_scene"), EditorNode::FILE_OPEN_PREV); + scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(EditorNode::FILE_OPEN_PREV), TTR("Undo Close Tab")); + if (!EditorNode::get_singleton()->has_previous_scenes()) { + scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(EditorNode::FILE_OPEN_PREV), true); + } + scene_tabs_context_menu->add_item(TTR("Close Other Tabs"), EditorNode::FILE_CLOSE_OTHERS); + if (EditorNode::get_editor_data().get_edited_scene_count() <= 1) { + scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(EditorNode::FILE_CLOSE_OTHERS), true); + } + scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right"), EditorNode::FILE_CLOSE_RIGHT); + if (EditorNode::get_editor_data().get_edited_scene_count() == tab_id + 1) { + scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(EditorNode::FILE_CLOSE_RIGHT), true); + } + scene_tabs_context_menu->add_item(TTR("Close All Tabs"), EditorNode::FILE_CLOSE_ALL); + } +} + +// TODO: This REALLY should be done in a better way than replacing all tabs after almost EVERY action. +void EditorSceneTabs::update_scene_tabs() { + tab_preview_panel->hide(); + + bool show_rb = EDITOR_GET("interface/scene_tabs/show_script_button"); + + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { + DisplayServer::get_singleton()->global_menu_clear("_dock"); + } + + // Get all scene names, which may be ambiguous. + Vector<String> disambiguated_scene_names; + Vector<String> full_path_names; + for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) { + disambiguated_scene_names.append(EditorNode::get_editor_data().get_scene_title(i)); + full_path_names.append(EditorNode::get_editor_data().get_scene_path(i)); + } + + EditorNode::disambiguate_filenames(full_path_names, disambiguated_scene_names); + + // Workaround to ignore the tab_changed signal from the first added tab. + scene_tabs->disconnect("tab_changed", callable_mp(this, &EditorSceneTabs::_scene_tab_changed)); + + scene_tabs->clear_tabs(); + Ref<Texture2D> script_icon = get_theme_icon(SNAME("Script"), SNAME("EditorIcons")); + for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) { + Node *type_node = EditorNode::get_editor_data().get_edited_scene_root(i); + Ref<Texture2D> icon; + if (type_node) { + icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node"); + } + + bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(EditorNode::get_editor_data().get_scene_history_id(i)); + scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon); + + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { + DisplayServer::get_singleton()->global_menu_add_item("_dock", EditorNode::get_editor_data().get_scene_title(i) + (unsaved ? "(*)" : ""), callable_mp(this, &EditorSceneTabs::_global_menu_scene), Callable(), i); + } + + if (show_rb && EditorNode::get_editor_data().get_scene_root_script(i).is_valid()) { + scene_tabs->set_tab_button_icon(i, script_icon); + } + } + + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { + DisplayServer::get_singleton()->global_menu_add_separator("_dock"); + DisplayServer::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), callable_mp(this, &EditorSceneTabs::_global_menu_new_window)); + } + + if (scene_tabs->get_tab_count() > 0) { + scene_tabs->set_current_tab(EditorNode::get_editor_data().get_edited_scene()); + } + + const Size2 add_button_size = Size2(scene_tab_add->get_size().x, scene_tabs->get_size().y); + if (scene_tabs->get_offset_buttons_visible()) { + // Move the add button to a fixed position. + if (scene_tab_add->get_parent() == scene_tabs) { + scene_tabs->remove_child(scene_tab_add); + scene_tab_add_ph->add_child(scene_tab_add); + scene_tab_add->set_rect(Rect2(Point2(), add_button_size)); + } + } else { + // Move the add button to be after the last tab. + if (scene_tab_add->get_parent() == scene_tab_add_ph) { + scene_tab_add_ph->remove_child(scene_tab_add); + scene_tabs->add_child(scene_tab_add); + } + + if (scene_tabs->get_tab_count() == 0) { + scene_tab_add->set_rect(Rect2(Point2(), add_button_size)); + return; + } + + Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1); + int hsep = scene_tabs->get_theme_constant(SNAME("h_separation")); + if (scene_tabs->is_layout_rtl()) { + scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - add_button_size.x - hsep, last_tab.position.y), add_button_size)); + } else { + scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y), add_button_size)); + } + } + + // Reconnect after everything is done. + scene_tabs->connect("tab_changed", callable_mp(this, &EditorSceneTabs::_scene_tab_changed)); +} + +void EditorSceneTabs::_tab_preview_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata) { + int p_tab = p_udata; + if (p_preview.is_valid()) { + tab_preview->set_texture(p_preview); + + Rect2 rect = scene_tabs->get_tab_rect(p_tab); + rect.position += scene_tabs->get_global_position(); + tab_preview_panel->set_global_position(rect.position + Vector2(0, rect.size.height)); + tab_preview_panel->show(); + } +} + +void EditorSceneTabs::_global_menu_scene(const Variant &p_tag) { + int idx = (int)p_tag; + scene_tabs->set_current_tab(idx); +} + +void EditorSceneTabs::_global_menu_new_window(const Variant &p_tag) { + if (OS::get_singleton()->get_main_loop()) { + List<String> args; + args.push_back("-p"); + OS::get_singleton()->create_instance(args); + } +} + +void EditorSceneTabs::shortcut_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + + Ref<InputEventKey> k = p_event; + if ((k.is_valid() && k->is_pressed() && !k->is_echo()) || Object::cast_to<InputEventShortcut>(*p_event)) { + if (ED_IS_SHORTCUT("editor/next_tab", p_event)) { + int next_tab = EditorNode::get_editor_data().get_edited_scene() + 1; + next_tab %= EditorNode::get_editor_data().get_edited_scene_count(); + _scene_tab_changed(next_tab); + } + if (ED_IS_SHORTCUT("editor/prev_tab", p_event)) { + int next_tab = EditorNode::get_editor_data().get_edited_scene() - 1; + next_tab = next_tab >= 0 ? next_tab : EditorNode::get_editor_data().get_edited_scene_count() - 1; + _scene_tab_changed(next_tab); + } + } +} + +void EditorSceneTabs::add_extra_button(Button *p_button) { + tabbar_container->add_child(p_button); +} + +void EditorSceneTabs::set_current_tab(int p_tab) { + scene_tabs->set_current_tab(p_tab); +} + +int EditorSceneTabs::get_current_tab() const { + return scene_tabs->get_current_tab(); +} + +void EditorSceneTabs::_bind_methods() { + ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab_index"))); + ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab_index"))); + + ClassDB::bind_method("_tab_preview_done", &EditorSceneTabs::_tab_preview_done); +} + +EditorSceneTabs::EditorSceneTabs() { + singleton = this; + + tabbar_panel = memnew(PanelContainer); + add_child(tabbar_panel); + tabbar_container = memnew(HBoxContainer); + tabbar_panel->add_child(tabbar_container); + + scene_tabs = memnew(TabBar); + scene_tabs->set_select_with_rmb(true); + scene_tabs->add_tab("unsaved"); + scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int()); + scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE); + scene_tabs->set_drag_to_rearrange_enabled(true); + scene_tabs->set_auto_translate(false); + scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL); + tabbar_container->add_child(scene_tabs); + + scene_tabs->connect("tab_changed", callable_mp(this, &EditorSceneTabs::_scene_tab_changed)); + scene_tabs->connect("tab_button_pressed", callable_mp(this, &EditorSceneTabs::_scene_tab_script_edited)); + scene_tabs->connect("tab_close_pressed", callable_mp(this, &EditorSceneTabs::_scene_tab_closed)); + scene_tabs->connect("tab_hovered", callable_mp(this, &EditorSceneTabs::_scene_tab_hovered)); + scene_tabs->connect("mouse_exited", callable_mp(this, &EditorSceneTabs::_scene_tab_exit)); + scene_tabs->connect("gui_input", callable_mp(this, &EditorSceneTabs::_scene_tab_input)); + scene_tabs->connect("active_tab_rearranged", callable_mp(this, &EditorSceneTabs::_reposition_active_tab)); + scene_tabs->connect("resized", callable_mp(this, &EditorSceneTabs::update_scene_tabs)); + + scene_tabs_context_menu = memnew(PopupMenu); + tabbar_container->add_child(scene_tabs_context_menu); + scene_tabs_context_menu->connect("id_pressed", callable_mp(EditorNode::get_singleton(), &EditorNode::trigger_menu_option).bind(false)); + + scene_tab_add = memnew(Button); + scene_tab_add->set_flat(true); + scene_tab_add->set_tooltip_text(TTR("Add a new scene.")); + scene_tabs->add_child(scene_tab_add); + scene_tab_add->connect("pressed", callable_mp(EditorNode::get_singleton(), &EditorNode::trigger_menu_option).bind(EditorNode::FILE_NEW_SCENE, false)); + + scene_tab_add_ph = memnew(Control); + scene_tab_add_ph->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size()); + tabbar_container->add_child(scene_tab_add_ph); + + // On-hover tab preview. + + Control *tab_preview_anchor = memnew(Control); + tab_preview_anchor->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + add_child(tab_preview_anchor); + + tab_preview_panel = memnew(Panel); + tab_preview_panel->set_size(Size2(100, 100) * EDSCALE); + tab_preview_panel->hide(); + tab_preview_panel->set_self_modulate(Color(1, 1, 1, 0.7)); + tab_preview_anchor->add_child(tab_preview_panel); + + tab_preview = memnew(TextureRect); + tab_preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); + tab_preview->set_size(Size2(96, 96) * EDSCALE); + tab_preview->set_position(Point2(2, 2) * EDSCALE); + tab_preview_panel->add_child(tab_preview); +} diff --git a/editor/gui/editor_scene_tabs.h b/editor/gui/editor_scene_tabs.h new file mode 100644 index 0000000000..c0dbfccdab --- /dev/null +++ b/editor/gui/editor_scene_tabs.h @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* editor_scene_tabs.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 EDITOR_SCENE_TABS_H +#define EDITOR_SCENE_TABS_H + +#include "scene/gui/margin_container.h" + +class Button; +class HBoxContainer; +class Panel; +class PanelContainer; +class PopupMenu; +class TabBar; +class TextureRect; + +class EditorSceneTabs : public MarginContainer { + GDCLASS(EditorSceneTabs, MarginContainer); + + static EditorSceneTabs *singleton; + + PanelContainer *tabbar_panel = nullptr; + HBoxContainer *tabbar_container = nullptr; + + TabBar *scene_tabs = nullptr; + PopupMenu *scene_tabs_context_menu = nullptr; + Button *scene_tab_add = nullptr; + Control *scene_tab_add_ph = nullptr; + + Panel *tab_preview_panel = nullptr; + TextureRect *tab_preview = nullptr; + + void _scene_tab_changed(int p_tab); + void _scene_tab_script_edited(int p_tab); + void _scene_tab_closed(int p_tab); + void _scene_tab_hovered(int p_tab); + void _scene_tab_exit(); + void _scene_tab_input(const Ref<InputEvent> &p_input); + + void _reposition_active_tab(int p_to_index); + void _update_context_menu(); + + void _tab_preview_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata); + + void _global_menu_scene(const Variant &p_tag); + void _global_menu_new_window(const Variant &p_tag); + + virtual void shortcut_input(const Ref<InputEvent> &p_event) override; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + static EditorSceneTabs *get_singleton() { return singleton; } + + void add_extra_button(Button *p_button); + + void set_current_tab(int p_tab); + int get_current_tab() const; + + void update_scene_tabs(); + + EditorSceneTabs(); +}; + +#endif // EDITOR_SCENE_TABS_H diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index d07e5dfa1a..0862af37b6 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -234,7 +234,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->add_button(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons")), BUTTON_SCRIPT); } else { //has no script (or script is a custom type) - item->set_custom_color(0, get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); + _set_item_custom_color(item, get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); item->set_selectable(0, false); if (!scr.is_null()) { // make sure to mark the script if a custom type @@ -251,11 +251,11 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { node_name += " " + TTR("(Connecting From)"); } item->set_text(0, node_name); - item->set_custom_color(0, accent); + _set_item_custom_color(item, accent); } } else if (part_of_subscene) { if (valid_types.size() == 0) { - item->set_custom_color(0, get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + _set_item_custom_color(item, get_theme_color(SNAME("warning_color"), SNAME("Editor"))); } } else if (marked.has(p_node)) { String node_name = p_node->get_name(); @@ -264,15 +264,15 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { } item->set_text(0, node_name); item->set_selectable(0, marked_selectable); - item->set_custom_color(0, get_theme_color(SNAME("accent_color"), SNAME("Editor"))); + _set_item_custom_color(item, get_theme_color(SNAME("accent_color"), SNAME("Editor"))); } else if (!p_node->can_process()) { - item->set_custom_color(0, get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); + _set_item_custom_color(item, get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); } else if (!marked_selectable && !marked_children_selectable) { Node *node = p_node; while (node) { if (marked.has(node)) { item->set_selectable(0, false); - item->set_custom_color(0, get_theme_color(SNAME("error_color"), SNAME("Editor"))); + _set_item_custom_color(item, get_theme_color(SNAME("error_color"), SNAME("Editor"))); break; } node = node->get_parent(); @@ -500,7 +500,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { } if (!valid) { - item->set_custom_color(0, get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); + _set_item_custom_color(item, get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); item->set_selectable(0, false); } } @@ -550,6 +550,11 @@ void SceneTreeEditor::_update_visibility_color(Node *p_node, TreeItem *p_item) { } } +void SceneTreeEditor::_set_item_custom_color(TreeItem *p_item, Color p_color) { + p_item->set_custom_color(0, p_color); + p_item->set_meta(SNAME("custom_color"), p_color); +} + void SceneTreeEditor::_node_script_changed(Node *p_node) { if (tree_dirty) { return; @@ -613,7 +618,7 @@ void SceneTreeEditor::_update_tree(bool p_scroll_to_selected) { updating_tree = false; tree_dirty = false; - if (!filter.strip_edges().is_empty()) { + if (!filter.strip_edges().is_empty() || !show_all_nodes) { _update_filter(nullptr, p_scroll_to_selected); } } @@ -639,22 +644,34 @@ bool SceneTreeEditor::_update_filter(TreeItem *p_parent, bool p_scroll_to_select PackedStringArray terms = filter.to_lower().split_spaces(); bool keep = _item_matches_all_terms(p_parent, terms); - p_parent->set_visible(keep_for_children || keep); + bool selectable = keep; if (keep && !valid_types.is_empty()) { - keep = false; + selectable = false; Node *n = get_node(p_parent->get_metadata(0)); for (const StringName &E : valid_types) { if (n->is_class(E) || EditorNode::get_singleton()->is_object_of_custom_type(n, E)) { - keep = true; + selectable = true; break; } } } - if (keep) { - p_parent->clear_custom_color(0); + if (show_all_nodes) { + p_parent->set_visible(keep_for_children || keep); + } else { + // Show only selectable nodes, or parents of selectable. + p_parent->set_visible(keep_for_children || selectable); + } + + if (selectable) { + Color custom_color = p_parent->get_meta(SNAME("custom_color"), Color(0, 0, 0, 0)); + if (custom_color == Color(0, 0, 0, 0)) { + p_parent->clear_custom_color(0); + } else { + p_parent->set_custom_color(0, custom_color); + } p_parent->set_selectable(0, true); } else if (keep_for_children) { p_parent->set_custom_color(0, get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); @@ -664,7 +681,7 @@ bool SceneTreeEditor::_update_filter(TreeItem *p_parent, bool p_scroll_to_select if (editor_selection) { Node *n = get_node(p_parent->get_metadata(0)); - if (keep) { + if (selectable) { if (p_scroll_to_selected && n && editor_selection->is_selected(n)) { tree->scroll_to_item(p_parent); } @@ -676,7 +693,7 @@ bool SceneTreeEditor::_update_filter(TreeItem *p_parent, bool p_scroll_to_select } } - return keep || keep_for_children; + return p_parent->is_visible(); } bool SceneTreeEditor::_item_matches_all_terms(TreeItem *p_item, PackedStringArray p_terms) { @@ -1086,6 +1103,11 @@ String SceneTreeEditor::get_filter_term_warning() { return filter_term_warning; } +void SceneTreeEditor::set_show_all_nodes(bool p_show_all_nodes) { + show_all_nodes = p_show_all_nodes; + _update_filter(nullptr, true); +} + void SceneTreeEditor::set_as_scene_tree_dock() { is_scene_tree_dock = true; } @@ -1494,6 +1516,11 @@ void SceneTreeDialog::popup_scenetree_dialog() { popup_centered_clamped(Size2(350, 700) * EDSCALE); } +void SceneTreeDialog::_show_all_nodes_changed(bool p_button_pressed) { + EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_all_nodes_for_node_selection", p_button_pressed); + tree->set_show_all_nodes(p_button_pressed); +} + void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) { if (p_valid.is_empty()) { return; @@ -1531,6 +1558,8 @@ void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) { label->set_text(type); label->set_auto_translate(false); } + + show_all_nodes->show(); } void SceneTreeDialog::_update_theme() { @@ -1598,17 +1627,31 @@ SceneTreeDialog::SceneTreeDialog() { content = memnew(VBoxContainer); add_child(content); + HBoxContainer *filter_hbc = memnew(HBoxContainer); + content->add_child(filter_hbc); + filter = memnew(LineEdit); filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); filter->set_placeholder(TTR("Filter Nodes")); filter->set_clear_button_enabled(true); filter->add_theme_constant_override("minimum_character_width", 0); filter->connect("text_changed", callable_mp(this, &SceneTreeDialog::_filter_changed)); - content->add_child(filter); + filter_hbc->add_child(filter); + + // Add 'Show All' button to HBoxContainer next to the filter, visible only when valid_types is defined. + show_all_nodes = memnew(CheckButton); + show_all_nodes->set_text(TTR("Show All")); + show_all_nodes->connect("toggled", callable_mp(this, &SceneTreeDialog::_show_all_nodes_changed)); + show_all_nodes->set_h_size_flags(Control::SIZE_SHRINK_BEGIN); + show_all_nodes->hide(); + filter_hbc->add_child(show_all_nodes); tree = memnew(SceneTreeEditor(false, false, true)); tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); tree->get_scene_tree()->connect("item_activated", callable_mp(this, &SceneTreeDialog::_select)); + // Initialize button state, must be done after the tree has been created to update its 'show_all_nodes' flag. + // This is also done before adding the tree to the content to avoid triggering unnecessary tree filtering. + show_all_nodes->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_all_nodes_for_node_selection", false)); content->add_child(tree); // Disable the OK button when no node is selected. diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h index c99f84912b..f38c76a21a 100644 --- a/editor/gui/scene_tree_editor.h +++ b/editor/gui/scene_tree_editor.h @@ -31,6 +31,7 @@ #ifndef SCENE_TREE_EDITOR_H #define SCENE_TREE_EDITOR_H +#include "scene/gui/check_button.h" #include "scene/gui/dialogs.h" #include "scene/gui/tree.h" @@ -61,6 +62,7 @@ class SceneTreeEditor : public Control { String filter; String filter_term_warning; + bool show_all_nodes = false; AcceptDialog *error = nullptr; AcceptDialog *warning = nullptr; @@ -115,6 +117,7 @@ class SceneTreeEditor : public Control { void _node_script_changed(Node *p_node); void _node_visibility_changed(Node *p_node); void _update_visibility_color(Node *p_node, TreeItem *p_item); + void _set_item_custom_color(TreeItem *p_item, Color p_color); void _selection_changed(); Node *get_scene_node(); @@ -142,6 +145,7 @@ public: void set_filter(const String &p_filter); String get_filter() const; String get_filter_term_warning(); + void set_show_all_nodes(bool p_show_all_nodes); void set_as_scene_tree_dock(); void set_display_foreign_nodes(bool p_display); @@ -176,12 +180,14 @@ class SceneTreeDialog : public ConfirmationDialog { VBoxContainer *content = nullptr; SceneTreeEditor *tree = nullptr; LineEdit *filter = nullptr; + CheckButton *show_all_nodes = nullptr; LocalVector<TextureRect *> valid_type_icons; void _select(); void _cancel(); void _selected_changed(); void _filter_changed(const String &p_filter); + void _show_all_nodes_changed(bool p_button_pressed); void _update_theme(); protected: diff --git a/editor/icons/Camera2D.svg b/editor/icons/Camera2D.svg index 24198a8f06..81e5cc2c8e 100644 --- a/editor/icons/Camera2D.svg +++ b/editor/icons/Camera2D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#8da5f3"><path d="m7 2-2 4h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-4zm1 5a3 3 0 0 1 3 3 3 3 0 0 1 -3 3 3 3 0 0 1 -3-3 3 3 0 0 1 3-3z"/><path d="m2 3h2v2h-2z"/></g></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z" fill="#8da5f3"/></svg> diff --git a/editor/icons/Camera3D.svg b/editor/icons/Camera3D.svg index 69d435ff17..bf61aa48fc 100644 --- a/editor/icons/Camera3D.svg +++ b/editor/icons/Camera3D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f" fill-opacity=".996078"><path d="m7 2-2 4h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-4zm1 5a3 3 0 0 1 3 3 3 3 0 0 1 -3 3 3 3 0 0 1 -3-3 3 3 0 0 1 3-3z"/><path d="m2 3h2v2h-2z"/></g></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z" fill="#fc7f7f"/></svg> diff --git a/editor/icons/CameraAttributes.svg b/editor/icons/CameraAttributes.svg index 72d88ace7a..459c64e11c 100644 --- a/editor/icons/CameraAttributes.svg +++ b/editor/icons/CameraAttributes.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#c38ef1"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 2a3 3 0 0 0-2 5.23V9L1 7v6l3-2v1a1 1 0 0 0 1 1h3a2 2 0 0 1 .73-1.24 1.83 1.83 0 0 1 1.828-3.143 1.8 1.8 0 0 1 3.313-.75 3 3 0 0 0-4.883-3.09A3 3 0 0 0 6 2z" fill="#e0e0e0"/><path d="M12.36 8.598a.533 3.2 0 0 0-.51 2.275 3.2.533 30 0 0-.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0-.515-.887.533 3.2 0 0 0-.51-2.275z" fill="#c38ef1"/></svg> diff --git a/editor/icons/CameraAttributesPhysical.svg b/editor/icons/CameraAttributesPhysical.svg index f75cd0dc44..6871177c13 100644 --- a/editor/icons/CameraAttributesPhysical.svg +++ b/editor/icons/CameraAttributesPhysical.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#ffca5f"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 2a3 3 0 0 0-2 5.23V9L1 7v6l3-2v1a1 1 0 0 0 1 1h3a2 2 0 0 1 .73-1.24 1.83 1.83 0 0 1 1.828-3.143 1.8 1.8 0 0 1 3.313-.75 3 3 0 0 0-4.883-3.09A3 3 0 0 0 6 2z" fill="#e0e0e0"/><path d="M12.36 8.598a.533 3.2 0 0 0-.51 2.275 3.2.533 30 0 0-.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0-.515-.887.533 3.2 0 0 0-.51-2.275z" fill="#5fb2ff"/></svg> diff --git a/editor/icons/CameraAttributesPractical.svg b/editor/icons/CameraAttributesPractical.svg index 0aed99056d..c33351af0f 100644 --- a/editor/icons/CameraAttributesPractical.svg +++ b/editor/icons/CameraAttributesPractical.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#5fb2ff"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 2a3 3 0 0 0-2 5.23V9L1 7v6l3-2v1a1 1 0 0 0 1 1h3a2 2 0 0 1 .73-1.24 1.83 1.83 0 0 1 1.828-3.143 1.8 1.8 0 0 1 3.313-.75 3 3 0 0 0-4.883-3.09A3 3 0 0 0 6 2z" fill="#e0e0e0"/><path d="M12.36 8.598a.533 3.2 0 0 0-.51 2.275 3.2.533 30 0 0-.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0-.515-.887.533 3.2 0 0 0-.51-2.275z" fill="#ffca5f"/></svg> diff --git a/editor/icons/CameraTexture.svg b/editor/icons/CameraTexture.svg index 5a050b900e..af9c32f1a7 100644 --- a/editor/icons/CameraTexture.svg +++ b/editor/icons/CameraTexture.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m2 1c-.5522847 0-1 .4477153-1 1v12c0 .552285.4477153 1 1 1h12c.552285 0 1-.447715 1-1v-12c0-.5522847-.447715-1-1-1zm1 2h10v8h-10z"/><g stroke-width="1.97307" transform="matrix(.50682329 0 0 .50682329 3.994604 2.971119)"><path d="m5.9298703 2.0300582-1.9730742 3.934252-2.95288.01033s-1.00000003.000001-1.00000003 1v5.9004198s-.00049262 1.000376.99950633 1.000351l13.7985506-.000351s1-.000001 1-1v-5.9004195s-.000008-.9979028-1-1l-2.95288-.01033-1.9730741-3.934252zm2.0701297 4.8922972c1.6568542 0 3 1.3431458 3 3 0 1.6568536-1.3431458 2.9999996-3 2.9999996s-3-1.343146-3-2.9999996c0-1.6568542 1.3431458-3 3-3z"/><path d="m.0106475 2.0300582 3.9461486-.0000002v1.9730745h-3.9461486z"/></g></g></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm4 1-.75 1H5a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H9.75L9 4zm1 2a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/ClippedCamera3D.svg b/editor/icons/ClippedCamera3D.svg deleted file mode 100644 index b1651aca6a..0000000000 --- a/editor/icons/ClippedCamera3D.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9.5 0a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V9l3 2V5l-3 2V5.23A3 3 0 0 0 9.5 0zm-3 12v4h3v-1h-2v-3zm-1 0h-2c-.5 0-1 .5-1 1v2c-.018.536.5 1 1 1h2v-1h-2v-2h2zm5 0v4h1v-1h1a1 1 0 0 0 1-1v-1c0-.552-.448-.994-1-1h-1zm1 1h1v1h-1z" fill="#fc7f7f"/></svg> diff --git a/editor/icons/GizmoCamera3D.svg b/editor/icons/GizmoCamera3D.svg index c22fa18ee4..1aa67bfed7 100644 --- a/editor/icons/GizmoCamera3D.svg +++ b/editor/icons/GizmoCamera3D.svg @@ -1 +1 @@ -<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m52.789062 12.285156-.068359.001953s-2.40509.20501-4.607422 1.613282c-2.202331 1.408271-4.620558 4.01711-6.382812 8.287109l-6.40625 15.527344h-.966797v-3.986328s.05404-2.69271-1.351563-5.503907c-1.405598-2.811197-5.028556-5.923828-10.076171-5.923828-5.047616 0-8.672527 3.112631-10.078126 5.923828s-1.351562 5.503907-1.351562 5.503907v4.796875c-2.8348528 1.102579-4.8904632 3.167645-5.8632812 5.113281-1.4055984 2.811197-1.3515624 5.503906-1.3515626 5.503906v59.427732c0 5.04762 3.1145841 8.67253 5.9257818 10.07813 2.811195 1.4056 5.503906 1.35156 5.503906 1.35156h96.820316c5.04761 0 8.67252-3.11458 10.07812-5.92578s1.35156-5.50391 1.35156-5.50391v-59.427732c0-5.047615-3.11458-8.672526-5.92578-10.078125-2.81119-1.405599-5.5039-1.349609-5.5039-1.349609h-19.859379l-6.40625-15.527344c-1.693114-4.102471-4.120301-6.670605-6.261719-8.09375-2.141416-1.423145-4.378906-1.779297-4.378906-1.779297l-.242187-.029297zm.201172 8h21.722657c.009-.0059.05394-.06976.867187.470703.864378.57445 2.152133 1.720094 3.292969 4.484375l8.451172 20.47461h25.210941s1.02269.05432 1.92578.505859c.90308.451543 1.5039.540926 1.5039 2.921875v59.427732s-.0563 1.0227-.50781 1.92578c-.45154.90309-.54093 1.50391-2.92187 1.50391h-96.820316s-1.022696-.0543-1.925782-.50586c-.903086-.45154-1.503906-.54288-1.503906-2.92383v-59.427732s.05627-1.022696.507813-1.925781c.451542-.903085.540927-1.501953 2.921875-1.501953h24.960937l8.451172-20.47461c1.241133-3.007304 2.535771-4.112924 3.296875-4.599609.756188-.483541.568815-.357095.566406-.355469zm12.009766 24c-18.572491 0-33.714844 15.142353-33.714844 33.714844s15.142352 33.71484 33.714844 33.71484 33.714844-15.142349 33.714844-33.71484-15.142353-33.714844-33.714844-33.714844zm0 8c14.24897 0 25.714844 11.465874 25.714844 25.714844s-11.465875 25.71484-25.714844 25.71484-25.714844-11.46587-25.714844-25.71484 11.465874-25.714844 25.714844-25.714844z" fill-opacity=".3"/><g fill="#f7f5cf" stroke-width="8.00001"><path d="m52.857144 16.285714s-4.425182.151262-7.428572 7.428572l-7.428571 17.999668h-22.285715c-7.4285711 0-7.4285711 7.428572-7.4285711 7.428572v59.428574c0 7.42857 7.4285711 7.42857 7.4285711 7.42857h96.821434c7.42857 0 7.42857-7.42857 7.42857-7.42857v-59.428574c0-7.428572-7.42857-7.428572-7.42857-7.428572h-22.535719l-7.428571-17.999668c-2.833953-6.866759-7.428572-7.428572-7.428572-7.428572zm12.142856 31.999999c16.410747 0 29.714286 13.303539 29.714286 29.714286s-13.303539 29.714291-29.714286 29.714291-29.714286-13.303544-29.714286-29.714291 13.303539-29.714286 29.714286-29.714286z"/><path d="m15.500291 33.728577h14.857143s0-7.428572-7.428571-7.428572c-7.428572 0-7.428572 7.428572-7.428572 7.428572z"/></g></svg> +<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M76 20a24 24 0 0 0-24 22.216 24 24 0 1 0-24 40.376V100a8 8 0 0 0 8 8h48a8 8 0 0 0 8-8v-8l24 16V60L92 76V61.84A24 24 0 0 0 76 20zM44 96a4 4 0 0 1-4-4V75a3 3 0 0 0-3-3 12 12 0 1 1 9-19 9.5 9.5 0 0 0 18-6 12 12 0 1 1 18 7 3 3 0 0 0-2 3v35a4 4 0 0 1-4 4z" stroke="#000" stroke-width="8" stroke-linejoin="round" stroke-opacity=".294"/><path d="M76 20a24 24 0 0 0-24 22.216 24 24 0 1 0-24 40.376V100a8 8 0 0 0 8 8h48a8 8 0 0 0 8-8v-8l24 16V60L92 76V61.84A24 24 0 0 0 76 20zM44 96a4 4 0 0 1-4-4V75a3 3 0 0 0-3-3 12 12 0 1 1 9-19 9.5 9.5 0 0 0 18-6 12 12 0 1 1 18 7 3 3 0 0 0-2 3v35a4 4 0 0 1-4 4z" fill="#f7f5cf"/></svg> diff --git a/editor/icons/XRCamera3D.svg b/editor/icons/XRCamera3D.svg index 88e11cfe34..501a86a9f7 100644 --- a/editor/icons/XRCamera3D.svg +++ b/editor/icons/XRCamera3D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><g fill-opacity=".996078" transform="translate(0 -2)"><path d="m7 2-2 3h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-3zm1 4c1.6568542 0 3 1.3431458 3 3 0 1.656854-1.3431458 3-3 3s-3-1.343146-3-3c0-1.6568542 1.3431458-3 3-3z"/><path d="m2 2h2v2h-2z"/></g><path d="m4 12v1c0 .552285.4477153 1 1 1-.5522847 0-1 .447715-1 1v1h1v-1h1v1h1v-1c0-.552285-.4477153-1-1-1 .5522847 0 1-.447715 1-1v-1h-1v1h-1v-1zm5 0v4h1v-1h1v1h1v-1c-.000919-.175812-.04817-.348275-.137-.5.08883-.151725.136081-.324188.137-.5v-1c0-.552285-.447715-1-1-1zm1 1h1v1h-1z"/></g></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9.5 0a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V9l3 2V5l-3 2V5.23A3 3 0 0 0 9.5 0zM4 12v1a1 1 0 0 0 1 1 1 1 0 0 0-1 1v1h1v-1h1v1h1v-1a1 1 0 0 0-1-1 1 1 0 0 0 1-1v-1H6v1H5v-1zm5 0v4h1v-1h1v1h1v-1a1 1 0 0 0-.137-.5A1 1 0 0 0 12 14v-1a1 1 0 0 0-1-1zm1 1h1v1h-1z" fill="#fc7f7f"/></svg> diff --git a/editor/import_defaults_editor.cpp b/editor/import_defaults_editor.cpp index 98a9bfe9dc..1cee083fba 100644 --- a/editor/import_defaults_editor.cpp +++ b/editor/import_defaults_editor.cpp @@ -109,8 +109,6 @@ void ImportDefaultsEditor::_save() { } else { ProjectSettings::get_singleton()->set("importer_defaults/" + settings->importer->get_importer_name(), Variant()); } - - emit_signal(SNAME("project_settings_changed")); } } @@ -169,15 +167,13 @@ void ImportDefaultsEditor::_importer_selected(int p_index) { void ImportDefaultsEditor::clear() { String last_selected; - if (importers->get_selected() > 0) { + + if (importers->get_selected() >= 0) { last_selected = importers->get_item_text(importers->get_selected()); } importers->clear(); - importers->add_item("<" + TTR("Select Importer") + ">"); - importers->set_item_disabled(0, true); - List<Ref<ResourceImporter>> importer_list; ResourceFormatImporter::get_singleton()->get_importers(&importer_list); Vector<String> names; @@ -187,19 +183,21 @@ void ImportDefaultsEditor::clear() { } names.sort(); + // `last_selected.is_empty()` means it's the first time being called. + if (last_selected.is_empty() && !names.is_empty()) { + last_selected = names[0]; + } + for (int i = 0; i < names.size(); i++) { importers->add_item(names[i]); if (names[i] == last_selected) { - importers->select(i + 1); + importers->select(i); + _update_importer(); } } } -void ImportDefaultsEditor::_bind_methods() { - ADD_SIGNAL(MethodInfo("project_settings_changed")); -} - ImportDefaultsEditor::ImportDefaultsEditor() { HBoxContainer *hb = memnew(HBoxContainer); hb->add_child(memnew(Label(TTR("Importer:")))); diff --git a/editor/import_defaults_editor.h b/editor/import_defaults_editor.h index 763cdf8bf1..ccc74f39a5 100644 --- a/editor/import_defaults_editor.h +++ b/editor/import_defaults_editor.h @@ -57,7 +57,6 @@ class ImportDefaultsEditor : public VBoxContainer { protected: void _notification(int p_what); - static void _bind_methods(); public: void clear(); diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 7d7b156bd0..c5a667cd5f 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -204,6 +204,7 @@ PluginConfigDialog::PluginConfigDialog() { name_edit = memnew(LineEdit); name_edit->set_placeholder("MyPlugin"); + name_edit->set_tooltip_text(TTR("Required. This name will be displayed in the list of plugins.")); name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(name_edit); @@ -215,6 +216,7 @@ PluginConfigDialog::PluginConfigDialog() { subfolder_edit = memnew(LineEdit); subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin"); + subfolder_edit->set_tooltip_text(TTR("Optional. The folder name should generally use `snake_case` naming (avoid spaces and special characters).\nIf left empty, the folder will be named after the plugin name converted to `snake_case`.")); subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(subfolder_edit); @@ -225,6 +227,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(desc_lb); desc_edit = memnew(TextEdit); + desc_edit->set_tooltip_text(TTR("Optional. This description should be kept relatively short (up to 5 lines).\nIt will display when hovering the plugin in the list of plugins.")); desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE); desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); desc_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -239,6 +242,7 @@ PluginConfigDialog::PluginConfigDialog() { author_edit = memnew(LineEdit); author_edit->set_placeholder("Godette"); + author_edit->set_tooltip_text(TTR("Optional. The author's username, full name, or organization name.")); author_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(author_edit); @@ -249,6 +253,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(version_lb); version_edit = memnew(LineEdit); + version_edit->set_tooltip_text(TTR("Optional. A human-readable version identifier used for informational purposes only.")); version_edit->set_placeholder("1.0"); version_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(version_edit); @@ -260,6 +265,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(script_option_lb); script_option_edit = memnew(OptionButton); + script_option_edit->set_tooltip_text(TTR("Required. The scripting language to use for the script.\nNote that a plugin may use several languages at once by adding more scripts to the plugin.")); int default_lang = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptLanguage *lang = ScriptServer::get_language(i); @@ -278,6 +284,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(script_lb); script_edit = memnew(LineEdit); + script_edit->set_tooltip_text(TTR("Optional. The path to the script (relative to the add-on folder). If left empty, will default to \"plugin.gd\".")); script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd"); script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(script_edit); diff --git a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp index bb3b8a124e..e2ad69ac15 100644 --- a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp +++ b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp @@ -116,7 +116,7 @@ void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_und } AudioStreamRandomizerEditorPlugin::AudioStreamRandomizerEditorPlugin() { - EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("AudioStreamRandomizer"), callable_mp(this, &AudioStreamRandomizerEditorPlugin::_move_stream_array_element)); + EditorNode::get_editor_data().add_move_array_element_function(SNAME("AudioStreamRandomizer"), callable_mp(this, &AudioStreamRandomizerEditorPlugin::_move_stream_array_element)); } AudioStreamRandomizerEditorPlugin::~AudioStreamRandomizerEditorPlugin() {} diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index c9c67f3d6b..832d0c204d 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -44,20 +44,15 @@ #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/scene_tree_dock.h" -#include "scene/2d/cpu_particles_2d.h" -#include "scene/2d/gpu_particles_2d.h" -#include "scene/2d/light_2d.h" #include "scene/2d/polygon_2d.h" #include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite_2d.h" #include "scene/2d/touch_screen_button.h" #include "scene/gui/flow_container.h" #include "scene/gui/grid_container.h" -#include "scene/gui/nine_patch_rect.h" #include "scene/gui/separator.h" #include "scene/gui/split_container.h" #include "scene/gui/subviewport_container.h" -#include "scene/gui/texture_rect.h" #include "scene/gui/view_panner.h" #include "scene/main/canvas_layer.h" #include "scene/main/window.h" @@ -259,7 +254,7 @@ bool CanvasItemEditor::_is_node_movable(const Node *p_node, bool p_popup_warning } if (Object::cast_to<Control>(p_node) && Object::cast_to<Container>(p_node->get_parent())) { if (p_popup_warning) { - EditorToaster::get_singleton()->popup_str("Children of a container get their position and size determined only by their parent.", EditorToaster::SEVERITY_WARNING); + EditorToaster::get_singleton()->popup_str(TTR("Children of a container get their position and size determined only by their parent."), EditorToaster::SEVERITY_WARNING); } return false; } @@ -1160,7 +1155,11 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve vguides.push_back(edited.x); undo_redo->create_action(TTR("Create Vertical Guide")); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", vguides); - undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides); + if (prev_vguides.is_empty()) { + undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "remove_meta", "_edit_vertical_guides_"); + } else { + undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides); + } undo_redo->add_undo_method(viewport, "queue_redraw"); undo_redo->commit_action(); } @@ -1193,7 +1192,11 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve hguides.push_back(edited.y); undo_redo->create_action(TTR("Create Horizontal Guide")); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides); - undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides); + if (prev_hguides.is_empty()) { + undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "remove_meta", "_edit_horizontal_guides_"); + } else { + undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides); + } undo_redo->add_undo_method(viewport, "queue_redraw"); undo_redo->commit_action(); } @@ -1221,8 +1224,16 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve undo_redo->create_action(TTR("Create Horizontal and Vertical Guides")); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", vguides); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides); - undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides); - undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides); + if (prev_vguides.is_empty()) { + undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "remove_meta", "_edit_vertical_guides_"); + } else { + undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides); + } + if (prev_hguides.is_empty()) { + undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "remove_meta", "_edit_horizontal_guides_"); + } else { + undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides); + } undo_redo->add_undo_method(viewport, "queue_redraw"); undo_redo->commit_action(); } @@ -5722,7 +5733,7 @@ void CanvasItemEditorViewport::_perform_drop_data() { } else { Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res)); if (texture != nullptr && texture.is_valid()) { - Node *child = _make_texture_node_type(default_texture_node_type); + Node *child = Object::cast_to<Node>(ClassDB::instantiate(default_texture_node_type)); _create_nodes(target_node, child, path, drop_pos); } } @@ -5853,30 +5864,6 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p } } -Node *CanvasItemEditorViewport::_make_texture_node_type(String texture_node_type) { - Node *node = nullptr; - if (texture_node_type == "Sprite2D") { - node = memnew(Sprite2D); - } else if (texture_node_type == "PointLight2D") { - node = memnew(PointLight2D); - } else if (texture_node_type == "CPUParticles2D") { - node = memnew(CPUParticles2D); - } else if (texture_node_type == "GPUParticles2D") { - node = memnew(GPUParticles2D); - } else if (texture_node_type == "Polygon2D") { - node = memnew(Polygon2D); - } else if (texture_node_type == "TouchScreenButton") { - node = memnew(TouchScreenButton); - } else if (texture_node_type == "TextureRect") { - node = memnew(TextureRect); - } else if (texture_node_type == "TextureButton") { - node = memnew(TextureButton); - } else if (texture_node_type == "NinePatchRect") { - node = memnew(NinePatchRect); - } - return node; -} - void CanvasItemEditorViewport::_update_theme() { List<BaseButton *> btn_list; button_group->get_buttons(&btn_list); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 2f97cda343..74f150fd65 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -613,7 +613,6 @@ class CanvasItemEditorViewport : public Control { void _on_select_type(Object *selected); void _on_change_type_confirmed(); void _on_change_type_closed(); - Node *_make_texture_node_type(String texture_node_type); void _create_preview(const Vector<String> &files) const; void _remove_preview(); diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp index 7be0d6c172..3ac9fee03f 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -33,8 +33,11 @@ #include "canvas_item_editor_plugin.h" #include "core/io/image_loader.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_file_dialog.h" +#include "editor/scene_tree_dock.h" #include "scene/2d/cpu_particles_2d.h" +#include "scene/2d/gpu_particles_2d.h" #include "scene/gui/check_box.h" #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" @@ -67,14 +70,26 @@ void CPUParticles2DEditorPlugin::_menu_callback(int p_idx) { switch (p_idx) { case MENU_LOAD_EMISSION_MASK: { file->popup_file_dialog(); - } break; case MENU_CLEAR_EMISSION_MASK: { emission_mask->popup_centered(); } break; case MENU_RESTART: { particles->restart(); - } + } break; + case MENU_CONVERT_TO_GPU_PARTICLES: { + GPUParticles2D *gpu_particles = memnew(GPUParticles2D); + gpu_particles->convert_from_particles(particles); + gpu_particles->set_name(particles->get_name()); + gpu_particles->set_transform(particles->get_transform()); + gpu_particles->set_visible(particles->is_visible()); + gpu_particles->set_process_mode(particles->get_process_mode()); + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Convert to GPUParticles3D")); + SceneTreeDock::get_singleton()->replace_node(particles, gpu_particles); + ur->commit_action(false); + } break; } } @@ -257,6 +272,7 @@ CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() { menu = memnew(MenuButton); menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART); menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); + menu->get_popup()->add_item(TTR("Convert to GPUParticles2D"), MENU_CONVERT_TO_GPU_PARTICLES); menu->set_text(TTR("CPUParticles2D")); menu->set_switch_on_hover(true); toolbar->add_child(menu); diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h index ff8e171208..5077827ce8 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.h +++ b/editor/plugins/cpu_particles_2d_editor_plugin.h @@ -49,7 +49,8 @@ class CPUParticles2DEditorPlugin : public EditorPlugin { enum { MENU_LOAD_EMISSION_MASK, MENU_CLEAR_EMISSION_MASK, - MENU_RESTART + MENU_RESTART, + MENU_CONVERT_TO_GPU_PARTICLES, }; enum EmissionMode { diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp index 6edfc2ef2e..1f1bc0e561 100644 --- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp @@ -31,8 +31,10 @@ #include "cpu_particles_3d_editor_plugin.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/gui/scene_tree_editor.h" #include "editor/plugins/node_3d_editor_plugin.h" +#include "editor/scene_tree_dock.h" #include "scene/gui/menu_button.h" void CPUParticles3DEditor::_node_removed(Node *p_node) { @@ -59,6 +61,20 @@ void CPUParticles3DEditor::_menu_option(int p_option) { case MENU_OPTION_RESTART: { node->restart(); + } break; + + case MENU_OPTION_CONVERT_TO_GPU_PARTICLES: { + GPUParticles3D *gpu_particles = memnew(GPUParticles3D); + gpu_particles->convert_from_particles(node); + gpu_particles->set_name(node->get_name()); + gpu_particles->set_transform(node->get_transform()); + gpu_particles->set_visible(node->is_visible()); + gpu_particles->set_process_mode(node->get_process_mode()); + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Convert to GPUParticles3D")); + SceneTreeDock::get_singleton()->replace_node(node, gpu_particles); + ur->commit_action(false); } break; } @@ -102,6 +118,7 @@ CPUParticles3DEditor::CPUParticles3DEditor() { options->set_text(TTR("CPUParticles3D")); options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); + options->get_popup()->add_item(TTR("Convert to GPUParticles3D"), MENU_OPTION_CONVERT_TO_GPU_PARTICLES); options->get_popup()->connect("id_pressed", callable_mp(this, &CPUParticles3DEditor::_menu_option)); } diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.h b/editor/plugins/cpu_particles_3d_editor_plugin.h index 894e0dfb31..6de23fc2b8 100644 --- a/editor/plugins/cpu_particles_3d_editor_plugin.h +++ b/editor/plugins/cpu_particles_3d_editor_plugin.h @@ -40,8 +40,8 @@ class CPUParticles3DEditor : public GPUParticles3DEditorBase { enum Menu { MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE, MENU_OPTION_CLEAR_EMISSION_VOLUME, - MENU_OPTION_RESTART - + MENU_OPTION_RESTART, + MENU_OPTION_CONVERT_TO_GPU_PARTICLES, }; CPUParticles3D *node = nullptr; diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index e24172e761..60119a5499 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -346,7 +346,7 @@ EditorInspectorPluginMaterial::EditorInspectorPluginMaterial() { env->set_ambient_source(Environment::AMBIENT_SOURCE_SKY); env->set_reflection_source(Environment::REFLECTION_SOURCE_SKY); - EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &EditorInspectorPluginMaterial::_undo_redo_inspector_callback)); + EditorNode::get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &EditorInspectorPluginMaterial::_undo_redo_inspector_callback)); } MaterialEditorPlugin::MaterialEditorPlugin() { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 65563bd1a3..d38890eb36 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2721,7 +2721,7 @@ void Node3DEditorViewport::_project_settings_changed() { void Node3DEditorViewport::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { - EditorNode::get_singleton()->connect("project_settings_changed", callable_mp(this, &Node3DEditorViewport::_project_settings_changed)); + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Node3DEditorViewport::_project_settings_changed)); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -7602,7 +7602,7 @@ void Node3DEditor::_notification(int p_what) { sun_state->set_custom_minimum_size(sun_vb->get_combined_minimum_size()); environ_state->set_custom_minimum_size(environ_vb->get_combined_minimum_size()); - EditorNode::get_singleton()->connect("project_settings_changed", callable_mp(this, &Node3DEditor::update_all_gizmos).bind(Variant())); + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Node3DEditor::update_all_gizmos).bind(Variant())); } break; case NOTIFICATION_ENTER_TREE: { diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index eb0e20a12e..39f4e4eb6a 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -52,6 +52,7 @@ #include "editor/find_in_files.h" #include "editor/gui/editor_file_dialog.h" #include "editor/gui/editor_run_bar.h" +#include "editor/gui/editor_toaster.h" #include "editor/inspector_dock.h" #include "editor/node_dock.h" #include "editor/plugins/shader_editor_plugin.h" @@ -1347,27 +1348,40 @@ void ScriptEditor::_menu_option(int p_option) { scr->reload(true); } break; + case FILE_RUN: { Ref<Script> scr = current->get_edited_resource(); if (scr == nullptr || scr.is_null()) { - EditorNode::get_singleton()->show_warning(TTR("Can't obtain the script for running.")); + EditorToaster::get_singleton()->popup_str(TTR("Cannot run the edited file because it's not a script."), EditorToaster::SEVERITY_WARNING); break; } - if (!scr->is_tool()) { - EditorNode::get_singleton()->show_warning(TTR("Script is not in tool mode, will not be able to run.")); - return; - } current->apply_code(); - Error err = scr->reload(false); //hard reload script before running always - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Script failed reloading, check console for errors.")); + Error err = scr->reload(false); // Always hard reload the script before running. + if (err != OK || !scr->is_valid()) { + EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it contains errors, check the output log."), EditorToaster::SEVERITY_WARNING); return; } + // Perform additional checks on the script to evaluate if it's runnable. + + bool is_runnable = true; if (!ClassDB::is_parent_class(scr->get_instance_base_type(), "EditorScript")) { - EditorNode::get_singleton()->show_warning(TTR("To run this script, it must inherit EditorScript and be set to tool mode.")); + is_runnable = false; + + EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it doesn't extend EditorScript."), EditorToaster::SEVERITY_WARNING); + } + if (!scr->is_tool()) { + is_runnable = false; + + if (scr->get_class() == "GDScript") { + EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it's not a tool script (add the @tool annotation at the top)."), EditorToaster::SEVERITY_WARNING); + } else { + EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it's not a tool script."), EditorToaster::SEVERITY_WARNING); + } + } + if (!is_runnable) { return; } @@ -1375,6 +1389,7 @@ void ScriptEditor::_menu_option(int p_option) { es->set_script(scr); es->run(); } break; + case FILE_CLOSE: { if (current->is_unsaved()) { _ask_close_current_unsaved_tab(current); @@ -3627,7 +3642,7 @@ void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_numb Ref<Resource> res = ResourceLoader::load(fpath); if (fpath.get_extension() == "gdshader") { - ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_singleton()->get_editor_data().get_editor("Shader")); + ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_editor_data().get_editor_by_name("Shader")); shader_editor->edit(res.ptr()); shader_editor->make_visible(true); shader_editor->get_shader_editor(res)->goto_line_selection(line_number - 1, begin, end); diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index ed98c7f85c..932a255982 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -1076,7 +1076,7 @@ TextShaderEditor::TextShaderEditor() { shader_editor->connect("show_warnings_panel", callable_mp(this, &TextShaderEditor::_show_warnings_panel)); shader_editor->connect("script_changed", callable_mp(this, &TextShaderEditor::apply_shaders)); EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextShaderEditor::_editor_settings_changed)); - ProjectSettingsEditor::get_singleton()->connect("confirmed", callable_mp(this, &TextShaderEditor::_project_settings_changed)); + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextShaderEditor::_project_settings_changed)); shader_editor->get_text_editor()->set_code_hint_draw_below(EDITOR_GET("text_editor/completion/put_callhint_tooltip_below_current_line")); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 19df31a0b3..124cd79126 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -38,64 +38,46 @@ #include "editor/editor_undo_redo_manager.h" #include "scene/gui/check_box.h" #include "scene/gui/option_button.h" +#include "scene/gui/panel_container.h" #include "scene/gui/separator.h" #include "scene/gui/spin_box.h" #include "scene/gui/view_panner.h" #include "scene/resources/atlas_texture.h" -void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) { - Vector2 line = (to - from).normalized() * 10; +Transform2D TextureRegionEditor::_get_offset_transform() const { + Transform2D mtx; + mtx.columns[2] = -draw_ofs * draw_zoom; + mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); - // Draw a translucent background line to make the foreground line visible on any background. - edit_draw->draw_line( - from, - to, - EditorNode::get_singleton()->get_theme_base()->get_theme_color(SNAME("mono_color"), SNAME("Editor")).inverted() * Color(1, 1, 1, 0.5), - Math::round(2 * EDSCALE)); - - while (from.distance_squared_to(to) > 200) { - edit_draw->draw_line( - from, - from + line, - EditorNode::get_singleton()->get_theme_base()->get_theme_color(SNAME("mono_color"), SNAME("Editor")), - Math::round(2 * EDSCALE)); - - from += line * 2; - } + return mtx; } -void TextureRegionEditor::_region_draw() { - Ref<Texture2D> base_tex = nullptr; - if (atlas_tex.is_valid()) { - base_tex = atlas_tex->get_atlas(); - } else if (node_sprite_2d) { - base_tex = node_sprite_2d->get_texture(); - } else if (node_sprite_3d) { - base_tex = node_sprite_3d->get_texture(); - } else if (node_ninepatch) { - base_tex = node_ninepatch->get_texture(); - } else if (obj_styleBox.is_valid()) { - base_tex = obj_styleBox->get_texture(); - } - - if (base_tex.is_null()) { +void TextureRegionEditor::_texture_preview_draw() { + Ref<Texture2D> object_texture = _get_edited_object_texture(); + if (object_texture.is_null()) { return; } - Transform2D mtx; - mtx.columns[2] = -draw_ofs * draw_zoom; - mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); + Transform2D mtx = _get_offset_transform(); - RS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), mtx); - edit_draw->draw_rect(Rect2(Point2(), preview_tex->get_size()), Color(0.5, 0.5, 0.5, 0.5), false); - edit_draw->draw_texture(preview_tex, Point2()); - RS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), Transform2D()); + RS::get_singleton()->canvas_item_add_set_transform(texture_preview->get_canvas_item(), mtx); + texture_preview->draw_rect(Rect2(Point2(), object_texture->get_size()), Color(0.5, 0.5, 0.5, 0.5), false); + texture_preview->draw_texture(object_texture, Point2()); + RS::get_singleton()->canvas_item_add_set_transform(texture_preview->get_canvas_item(), Transform2D()); +} + +void TextureRegionEditor::_texture_overlay_draw() { + Ref<Texture2D> object_texture = _get_edited_object_texture(); + if (object_texture.is_null()) { + return; + } + Transform2D mtx = _get_offset_transform(); const Color color = get_theme_color(SNAME("mono_color"), SNAME("Editor")); if (snap_mode == SNAP_GRID) { const Color grid_color = Color(color.r, color.g, color.b, color.a * 0.15); - Size2 s = edit_draw->get_size(); + Size2 s = texture_overlay->get_size(); int last_cell = 0; if (snap_step.x != 0) { @@ -106,7 +88,7 @@ void TextureRegionEditor::_region_draw() { last_cell = cell; } if (last_cell != cell) { - edit_draw->draw_line(Point2(i, 0), Point2(i, s.height), grid_color); + texture_overlay->draw_line(Point2(i, 0), Point2(i, s.height), grid_color); } last_cell = cell; } @@ -117,7 +99,7 @@ void TextureRegionEditor::_region_draw() { last_cell = cell; } if (last_cell != cell) { - edit_draw->draw_rect(Rect2(i - snap_separation.x * draw_zoom, 0, snap_separation.x * draw_zoom, s.height), grid_color); + texture_overlay->draw_rect(Rect2(i - snap_separation.x * draw_zoom, 0, snap_separation.x * draw_zoom, s.height), grid_color); } last_cell = cell; } @@ -132,7 +114,7 @@ void TextureRegionEditor::_region_draw() { last_cell = cell; } if (last_cell != cell) { - edit_draw->draw_line(Point2(0, i), Point2(s.width, i), grid_color); + texture_overlay->draw_line(Point2(0, i), Point2(s.width, i), grid_color); } last_cell = cell; } @@ -143,7 +125,7 @@ void TextureRegionEditor::_region_draw() { last_cell = cell; } if (last_cell != cell) { - edit_draw->draw_rect(Rect2(0, i - snap_separation.y * draw_zoom, s.width, snap_separation.y * draw_zoom), grid_color); + texture_overlay->draw_rect(Rect2(0, i - snap_separation.y * draw_zoom, s.width, snap_separation.y * draw_zoom), grid_color); } last_cell = cell; } @@ -159,14 +141,14 @@ void TextureRegionEditor::_region_draw() { }; for (int i = 0; i < 4; i++) { int next = (i + 1) % 4; - edit_draw->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, Color(0.3, 0.7, 1, 1), 2); + texture_overlay->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, Color(0.3, 0.7, 1, 1), 2); } } } Ref<Texture2D> select_handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); - Rect2 scroll_rect(Point2(), base_tex->get_size()); + Rect2 scroll_rect(Point2(), object_texture->get_size()); const Vector2 raw_endpoints[4] = { rect.position, @@ -187,23 +169,23 @@ void TextureRegionEditor::_region_draw() { Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized(); ofs *= Math_SQRT2 * (select_handle->get_size().width / 2); - edit_draw->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, color, 2); + texture_overlay->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, color, 2); if (snap_mode != SNAP_AUTOSLICE) { - edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); + texture_overlay->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); } ofs = (endpoints[next] - endpoints[i]) / 2; ofs += (endpoints[next] - endpoints[i]).orthogonal().normalized() * (select_handle->get_size().width / 2); if (snap_mode != SNAP_AUTOSLICE) { - edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); + texture_overlay->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); } scroll_rect.expand_to(raw_endpoints[i]); } - const Size2 scroll_margin = edit_draw->get_size() / draw_zoom; + const Size2 scroll_margin = texture_overlay->get_size() / draw_zoom; scroll_rect.position -= scroll_margin; scroll_rect.size += scroll_margin * 2; @@ -244,21 +226,22 @@ void TextureRegionEditor::_region_draw() { vscroll->set_value((vscroll->get_min() + vscroll->get_max() - vscroll->get_page()) / 2); // This ensures that the view is updated correctly. callable_mp(this, &TextureRegionEditor::_pan_callback).bind(Vector2(1, 0)).call_deferred(); + callable_mp(this, &TextureRegionEditor::_scroll_changed).bind(0.0).call_deferred(); request_center = false; } - if (node_ninepatch || obj_styleBox.is_valid()) { + if (node_ninepatch || res_stylebox.is_valid()) { float margins[4] = { 0 }; if (node_ninepatch) { margins[0] = node_ninepatch->get_patch_margin(SIDE_TOP); margins[1] = node_ninepatch->get_patch_margin(SIDE_BOTTOM); margins[2] = node_ninepatch->get_patch_margin(SIDE_LEFT); margins[3] = node_ninepatch->get_patch_margin(SIDE_RIGHT); - } else if (obj_styleBox.is_valid()) { - margins[0] = obj_styleBox->get_texture_margin(SIDE_TOP); - margins[1] = obj_styleBox->get_texture_margin(SIDE_BOTTOM); - margins[2] = obj_styleBox->get_texture_margin(SIDE_LEFT); - margins[3] = obj_styleBox->get_texture_margin(SIDE_RIGHT); + } else if (res_stylebox.is_valid()) { + margins[0] = res_stylebox->get_texture_margin(SIDE_TOP); + margins[1] = res_stylebox->get_texture_margin(SIDE_BOTTOM); + margins[2] = res_stylebox->get_texture_margin(SIDE_LEFT); + margins[3] = res_stylebox->get_texture_margin(SIDE_RIGHT); } Vector2 pos[4] = { @@ -268,14 +251,36 @@ void TextureRegionEditor::_region_draw() { -mtx.basis_xform(Vector2(margins[3], 0)) + Vector2(endpoints[2].x - draw_ofs.x * draw_zoom, 0) }; - draw_margin_line(edit_draw, pos[0], pos[0] + Vector2(edit_draw->get_size().x, 0)); - draw_margin_line(edit_draw, pos[1], pos[1] + Vector2(edit_draw->get_size().x, 0)); - draw_margin_line(edit_draw, pos[2], pos[2] + Vector2(0, edit_draw->get_size().y)); - draw_margin_line(edit_draw, pos[3], pos[3] + Vector2(0, edit_draw->get_size().y)); + _draw_margin_line(pos[0], pos[0] + Vector2(texture_overlay->get_size().x, 0)); + _draw_margin_line(pos[1], pos[1] + Vector2(texture_overlay->get_size().x, 0)); + _draw_margin_line(pos[2], pos[2] + Vector2(0, texture_overlay->get_size().y)); + _draw_margin_line(pos[3], pos[3] + Vector2(0, texture_overlay->get_size().y)); } } -void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { +void TextureRegionEditor::_draw_margin_line(Vector2 p_from, Vector2 p_to) { + // Margin line is a dashed line with a normalized dash length. This method works + // for both vertical and horizontal lines. + + Vector2 dash_size = (p_to - p_from).normalized() * 10; + const int dash_thickness = Math::round(2 * EDSCALE); + const Color dash_color = get_theme_color(SNAME("mono_color"), SNAME("Editor")); + const Color dash_bg_color = dash_color.inverted() * Color(1, 1, 1, 0.5); + const int line_threshold = 200; + + // Draw a translucent background line to make the foreground line visible on any background. + texture_overlay->draw_line(p_from, p_to, dash_bg_color, dash_thickness); + + Vector2 dash_start = p_from; + while (dash_start.distance_squared_to(p_to) > line_threshold) { + texture_overlay->draw_line(dash_start, dash_start + dash_size, dash_color, dash_thickness); + + // Skip two size lengths, one for the drawn dash and one for the gap. + dash_start += dash_size * 2; + } +} + +void TextureRegionEditor::_texture_overlay_input(const Ref<InputEvent> &p_input) { if (panner->gui_input(p_input)) { return; } @@ -284,112 +289,28 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { mtx.columns[2] = -draw_ofs * draw_zoom; mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); - const real_t handle_radius = 8 * EDSCALE; - const real_t handle_offset = 4 * EDSCALE; - - // Position of selection handles. - const Vector2 endpoints[8] = { - mtx.xform(rect.position) + Vector2(-handle_offset, -handle_offset), - mtx.xform(rect.position + Vector2(rect.size.x / 2, 0)) + Vector2(0, -handle_offset), - mtx.xform(rect.position + Vector2(rect.size.x, 0)) + Vector2(handle_offset, -handle_offset), - mtx.xform(rect.position + Vector2(rect.size.x, rect.size.y / 2)) + Vector2(handle_offset, 0), - mtx.xform(rect.position + rect.size) + Vector2(handle_offset, handle_offset), - mtx.xform(rect.position + Vector2(rect.size.x / 2, rect.size.y)) + Vector2(0, handle_offset), - mtx.xform(rect.position + Vector2(0, rect.size.y)) + Vector2(-handle_offset, handle_offset), - mtx.xform(rect.position + Vector2(0, rect.size.y / 2)) + Vector2(-handle_offset, 0) - }; - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); Ref<InputEventMouseButton> mb = p_input; if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed() && !panner->is_panning()) { - if (node_ninepatch || obj_styleBox.is_valid()) { - edited_margin = -1; - float margins[4] = { 0 }; - if (node_ninepatch) { - margins[0] = node_ninepatch->get_patch_margin(SIDE_TOP); - margins[1] = node_ninepatch->get_patch_margin(SIDE_BOTTOM); - margins[2] = node_ninepatch->get_patch_margin(SIDE_LEFT); - margins[3] = node_ninepatch->get_patch_margin(SIDE_RIGHT); - } else if (obj_styleBox.is_valid()) { - margins[0] = obj_styleBox->get_texture_margin(SIDE_TOP); - margins[1] = obj_styleBox->get_texture_margin(SIDE_BOTTOM); - margins[2] = obj_styleBox->get_texture_margin(SIDE_LEFT); - margins[3] = obj_styleBox->get_texture_margin(SIDE_RIGHT); - } - - Vector2 pos[4] = { - mtx.basis_xform(rect.position + Vector2(0, margins[0])) - draw_ofs * draw_zoom, - mtx.basis_xform(rect.position + rect.size - Vector2(0, margins[1])) - draw_ofs * draw_zoom, - mtx.basis_xform(rect.position + Vector2(margins[2], 0)) - draw_ofs * draw_zoom, - mtx.basis_xform(rect.position + rect.size - Vector2(margins[3], 0)) - draw_ofs * draw_zoom + // Check if we click on any handle first. + { + const real_t handle_radius = 16 * EDSCALE; + const real_t handle_offset = 8 * EDSCALE; + + // Position of selection handles. + const Vector2 endpoints[8] = { + mtx.xform(rect.position) + Vector2(-handle_offset, -handle_offset), + mtx.xform(rect.position + Vector2(rect.size.x / 2, 0)) + Vector2(0, -handle_offset), + mtx.xform(rect.position + Vector2(rect.size.x, 0)) + Vector2(handle_offset, -handle_offset), + mtx.xform(rect.position + Vector2(rect.size.x, rect.size.y / 2)) + Vector2(handle_offset, 0), + mtx.xform(rect.position + rect.size) + Vector2(handle_offset, handle_offset), + mtx.xform(rect.position + Vector2(rect.size.x / 2, rect.size.y)) + Vector2(0, handle_offset), + mtx.xform(rect.position + Vector2(0, rect.size.y)) + Vector2(-handle_offset, handle_offset), + mtx.xform(rect.position + Vector2(0, rect.size.y / 2)) + Vector2(-handle_offset, 0) }; - if (Math::abs(mb->get_position().y - pos[0].y) < 8) { - edited_margin = 0; - prev_margin = margins[0]; - } else if (Math::abs(mb->get_position().y - pos[1].y) < 8) { - edited_margin = 1; - prev_margin = margins[1]; - } else if (Math::abs(mb->get_position().x - pos[2].x) < 8) { - edited_margin = 2; - prev_margin = margins[2]; - } else if (Math::abs(mb->get_position().x - pos[3].x) < 8) { - edited_margin = 3; - prev_margin = margins[3]; - } - if (edited_margin >= 0) { - drag_from = mb->get_position(); - drag = true; - } - } - if (edited_margin < 0 && snap_mode == SNAP_AUTOSLICE) { - Vector2 point = mtx.affine_inverse().xform(mb->get_position()); - for (const Rect2 &E : autoslice_cache) { - if (E.has_point(point)) { - rect = E; - if (Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !(Input::get_singleton()->is_key_pressed(Key(Key::SHIFT | Key::ALT)))) { - Rect2 r; - if (atlas_tex.is_valid()) { - r = atlas_tex->get_region(); - } else if (node_sprite_2d) { - r = node_sprite_2d->get_region_rect(); - } else if (node_sprite_3d) { - r = node_sprite_3d->get_region_rect(); - } else if (node_ninepatch) { - r = node_ninepatch->get_region_rect(); - } else if (obj_styleBox.is_valid()) { - r = obj_styleBox->get_region_rect(); - } - rect.expand_to(r.position); - rect.expand_to(r.get_end()); - } - undo_redo->create_action(TTR("Set Region Rect")); - if (atlas_tex.is_valid()) { - undo_redo->add_do_method(atlas_tex.ptr(), "set_region", rect); - undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region()); - } else if (node_sprite_2d) { - undo_redo->add_do_method(node_sprite_2d, "set_region_rect", rect); - undo_redo->add_undo_method(node_sprite_2d, "set_region_rect", node_sprite_2d->get_region_rect()); - } else if (node_sprite_3d) { - undo_redo->add_do_method(node_sprite_3d, "set_region_rect", rect); - undo_redo->add_undo_method(node_sprite_3d, "set_region_rect", node_sprite_3d->get_region_rect()); - } else if (node_ninepatch) { - undo_redo->add_do_method(node_ninepatch, "set_region_rect", rect); - undo_redo->add_undo_method(node_ninepatch, "set_region_rect", node_ninepatch->get_region_rect()); - } else if (obj_styleBox.is_valid()) { - undo_redo->add_do_method(obj_styleBox.ptr(), "set_region_rect", rect); - undo_redo->add_undo_method(obj_styleBox.ptr(), "set_region_rect", obj_styleBox->get_region_rect()); - } - undo_redo->add_do_method(this, "_update_rect"); - undo_redo->add_undo_method(this, "_update_rect"); - undo_redo->add_do_method(edit_draw, "queue_redraw"); - undo_redo->add_undo_method(edit_draw, "queue_redraw"); - undo_redo->commit_action(); - break; - } - } - } else if (edited_margin < 0) { + drag_from = mtx.affine_inverse().xform(mb->get_position()); if (snap_mode == SNAP_PIXEL) { drag_from = drag_from.snapped(Vector2(1, 1)); @@ -397,17 +318,8 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { drag_from = snap_point(drag_from); } drag = true; - if (atlas_tex.is_valid()) { - rect_prev = atlas_tex->get_region(); - } else if (node_sprite_2d) { - rect_prev = node_sprite_2d->get_region_rect(); - } else if (node_sprite_3d) { - rect_prev = node_sprite_3d->get_region_rect(); - } else if (node_ninepatch) { - rect_prev = node_ninepatch->get_region_rect(); - } else if (obj_styleBox.is_valid()) { - rect_prev = obj_styleBox->get_region_rect(); - } + + rect_prev = _get_edited_object_region(); for (int i = 0; i < 8; i++) { Vector2 tuv = endpoints[i]; @@ -415,10 +327,109 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { drag_index = i; } } + } + + // We didn't hit any handle, try other options. + if (drag_index < 0) { + if (node_ninepatch || res_stylebox.is_valid()) { + // For ninepatchable objects check if we are clicking on margin bars. + + edited_margin = -1; + float margins[4] = { 0 }; + if (node_ninepatch) { + margins[0] = node_ninepatch->get_patch_margin(SIDE_TOP); + margins[1] = node_ninepatch->get_patch_margin(SIDE_BOTTOM); + margins[2] = node_ninepatch->get_patch_margin(SIDE_LEFT); + margins[3] = node_ninepatch->get_patch_margin(SIDE_RIGHT); + } else if (res_stylebox.is_valid()) { + margins[0] = res_stylebox->get_texture_margin(SIDE_TOP); + margins[1] = res_stylebox->get_texture_margin(SIDE_BOTTOM); + margins[2] = res_stylebox->get_texture_margin(SIDE_LEFT); + margins[3] = res_stylebox->get_texture_margin(SIDE_RIGHT); + } - if (drag_index == -1) { - creating = true; - rect = Rect2(drag_from, Size2()); + Vector2 pos[4] = { + mtx.basis_xform(rect.position + Vector2(0, margins[0])) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + rect.size - Vector2(0, margins[1])) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + Vector2(margins[2], 0)) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + rect.size - Vector2(margins[3], 0)) - draw_ofs * draw_zoom + }; + if (Math::abs(mb->get_position().y - pos[0].y) < 8) { + edited_margin = 0; + prev_margin = margins[0]; + } else if (Math::abs(mb->get_position().y - pos[1].y) < 8) { + edited_margin = 1; + prev_margin = margins[1]; + } else if (Math::abs(mb->get_position().x - pos[2].x) < 8) { + edited_margin = 2; + prev_margin = margins[2]; + } else if (Math::abs(mb->get_position().x - pos[3].x) < 8) { + edited_margin = 3; + prev_margin = margins[3]; + } + if (edited_margin >= 0) { + drag_from = mb->get_position(); + drag = true; + } + } + + if (edited_margin < 0 && snap_mode == SNAP_AUTOSLICE) { + // We didn't hit anything, but we're in the autoslice mode. Handle it. + + Vector2 point = mtx.affine_inverse().xform(mb->get_position()); + for (const Rect2 &E : autoslice_cache) { + if (E.has_point(point)) { + rect = E; + if (Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !(Input::get_singleton()->is_key_pressed(Key(Key::SHIFT | Key::ALT)))) { + Rect2 r; + if (node_sprite_2d) { + r = node_sprite_2d->get_region_rect(); + } else if (node_sprite_3d) { + r = node_sprite_3d->get_region_rect(); + } else if (node_ninepatch) { + r = node_ninepatch->get_region_rect(); + } else if (res_stylebox.is_valid()) { + r = res_stylebox->get_region_rect(); + } else if (res_atlas_texture.is_valid()) { + r = res_atlas_texture->get_region(); + } + rect.expand_to(r.position); + rect.expand_to(r.get_end()); + } + + undo_redo->create_action(TTR("Set Region Rect")); + if (node_sprite_2d) { + undo_redo->add_do_method(node_sprite_2d, "set_region_rect", rect); + undo_redo->add_undo_method(node_sprite_2d, "set_region_rect", node_sprite_2d->get_region_rect()); + } else if (node_sprite_3d) { + undo_redo->add_do_method(node_sprite_3d, "set_region_rect", rect); + undo_redo->add_undo_method(node_sprite_3d, "set_region_rect", node_sprite_3d->get_region_rect()); + } else if (node_ninepatch) { + undo_redo->add_do_method(node_ninepatch, "set_region_rect", rect); + undo_redo->add_undo_method(node_ninepatch, "set_region_rect", node_ninepatch->get_region_rect()); + } else if (res_stylebox.is_valid()) { + undo_redo->add_do_method(res_stylebox.ptr(), "set_region_rect", rect); + undo_redo->add_undo_method(res_stylebox.ptr(), "set_region_rect", res_stylebox->get_region_rect()); + } else if (res_atlas_texture.is_valid()) { + undo_redo->add_do_method(res_atlas_texture.ptr(), "set_region", rect); + undo_redo->add_undo_method(res_atlas_texture.ptr(), "set_region", res_atlas_texture->get_region()); + } + + undo_redo->add_do_method(this, "_update_rect"); + undo_redo->add_undo_method(this, "_update_rect"); + undo_redo->add_do_method(texture_overlay, "queue_redraw"); + undo_redo->add_undo_method(texture_overlay, "queue_redraw"); + undo_redo->commit_action(); + break; + } + } + } else if (edited_margin < 0) { + // We didn't hit anything and it's not autoslice, which means we try to create a new region. + + if (drag_index == -1) { + creating = true; + rect = Rect2(drag_from, Size2()); + } } } @@ -429,18 +440,15 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { if (node_ninepatch) { undo_redo->add_do_method(node_ninepatch, "set_patch_margin", side[edited_margin], node_ninepatch->get_patch_margin(side[edited_margin])); undo_redo->add_undo_method(node_ninepatch, "set_patch_margin", side[edited_margin], prev_margin); - } else if (obj_styleBox.is_valid()) { - undo_redo->add_do_method(obj_styleBox.ptr(), "set_texture_margin", side[edited_margin], obj_styleBox->get_texture_margin(side[edited_margin])); - undo_redo->add_undo_method(obj_styleBox.ptr(), "set_texture_margin", side[edited_margin], prev_margin); - obj_styleBox->emit_changed(); + } else if (res_stylebox.is_valid()) { + undo_redo->add_do_method(res_stylebox.ptr(), "set_texture_margin", side[edited_margin], res_stylebox->get_texture_margin(side[edited_margin])); + undo_redo->add_undo_method(res_stylebox.ptr(), "set_texture_margin", side[edited_margin], prev_margin); + res_stylebox->emit_changed(); } edited_margin = -1; } else { undo_redo->create_action(TTR("Set Region Rect")); - if (atlas_tex.is_valid()) { - undo_redo->add_do_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region()); - undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", rect_prev); - } else if (node_sprite_2d) { + if (node_sprite_2d) { undo_redo->add_do_method(node_sprite_2d, "set_region_rect", node_sprite_2d->get_region_rect()); undo_redo->add_undo_method(node_sprite_2d, "set_region_rect", rect_prev); } else if (node_sprite_3d) { @@ -449,16 +457,19 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } else if (node_ninepatch) { undo_redo->add_do_method(node_ninepatch, "set_region_rect", node_ninepatch->get_region_rect()); undo_redo->add_undo_method(node_ninepatch, "set_region_rect", rect_prev); - } else if (obj_styleBox.is_valid()) { - undo_redo->add_do_method(obj_styleBox.ptr(), "set_region_rect", obj_styleBox->get_region_rect()); - undo_redo->add_undo_method(obj_styleBox.ptr(), "set_region_rect", rect_prev); + } else if (res_stylebox.is_valid()) { + undo_redo->add_do_method(res_stylebox.ptr(), "set_region_rect", res_stylebox->get_region_rect()); + undo_redo->add_undo_method(res_stylebox.ptr(), "set_region_rect", rect_prev); + } else if (res_atlas_texture.is_valid()) { + undo_redo->add_do_method(res_atlas_texture.ptr(), "set_region", res_atlas_texture->get_region()); + undo_redo->add_undo_method(res_atlas_texture.ptr(), "set_region", rect_prev); } drag_index = -1; } undo_redo->add_do_method(this, "_update_rect"); undo_redo->add_undo_method(this, "_update_rect"); - undo_redo->add_do_method(edit_draw, "queue_redraw"); - undo_redo->add_undo_method(edit_draw, "queue_redraw"); + undo_redo->add_do_method(texture_overlay, "queue_redraw"); + undo_redo->add_undo_method(texture_overlay, "queue_redraw"); undo_redo->commit_action(); drag = false; creating = false; @@ -472,14 +483,15 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { if (node_ninepatch) { node_ninepatch->set_patch_margin(side[edited_margin], prev_margin); } - if (obj_styleBox.is_valid()) { - obj_styleBox->set_texture_margin(side[edited_margin], prev_margin); + if (res_stylebox.is_valid()) { + res_stylebox->set_texture_margin(side[edited_margin], prev_margin); } edited_margin = -1; } else { - apply_rect(rect_prev); + _apply_rect(rect_prev); rect = rect_prev; - edit_draw->queue_redraw(); + texture_preview->queue_redraw(); + texture_overlay->queue_redraw(); drag_index = -1; } } @@ -533,8 +545,8 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { if (node_ninepatch) { node_ninepatch->set_patch_margin(side[edited_margin], new_margin); } - if (obj_styleBox.is_valid()) { - obj_styleBox->set_texture_margin(side[edited_margin], new_margin); + if (res_stylebox.is_valid()) { + res_stylebox->set_texture_margin(side[edited_margin], new_margin); } } else { Vector2 new_pos = mtx.affine_inverse().xform(mm->get_position()); @@ -547,8 +559,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { if (creating) { rect = Rect2(drag_from, Size2()); rect.expand_to(new_pos); - apply_rect(rect); - edit_draw->queue_redraw(); + _apply_rect(rect); + texture_preview->queue_redraw(); + texture_overlay->queue_redraw(); return; } @@ -557,53 +570,54 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { Vector2 p = rect_prev.get_end(); rect = Rect2(p, Size2()); rect.expand_to(new_pos); - apply_rect(rect); + _apply_rect(rect); } break; case 1: { Vector2 p = rect_prev.position + Vector2(0, rect_prev.size.y); rect = Rect2(p, Size2(rect_prev.size.x, 0)); rect.expand_to(new_pos); - apply_rect(rect); + _apply_rect(rect); } break; case 2: { Vector2 p = rect_prev.position + Vector2(0, rect_prev.size.y); rect = Rect2(p, Size2()); rect.expand_to(new_pos); - apply_rect(rect); + _apply_rect(rect); } break; case 3: { Vector2 p = rect_prev.position; rect = Rect2(p, Size2(0, rect_prev.size.y)); rect.expand_to(new_pos); - apply_rect(rect); + _apply_rect(rect); } break; case 4: { Vector2 p = rect_prev.position; rect = Rect2(p, Size2()); rect.expand_to(new_pos); - apply_rect(rect); + _apply_rect(rect); } break; case 5: { Vector2 p = rect_prev.position; rect = Rect2(p, Size2(rect_prev.size.x, 0)); rect.expand_to(new_pos); - apply_rect(rect); + _apply_rect(rect); } break; case 6: { Vector2 p = rect_prev.position + Vector2(rect_prev.size.x, 0); rect = Rect2(p, Size2()); rect.expand_to(new_pos); - apply_rect(rect); + _apply_rect(rect); } break; case 7: { Vector2 p = rect_prev.position + Vector2(rect_prev.size.x, 0); rect = Rect2(p, Size2(0, rect_prev.size.y)); rect.expand_to(new_pos); - apply_rect(rect); + _apply_rect(rect); } break; } } - edit_draw->queue_redraw(); + texture_preview->queue_redraw(); + texture_overlay->queue_redraw(); } } @@ -636,53 +650,50 @@ void TextureRegionEditor::_scroll_changed(float) { draw_ofs.x = hscroll->get_value(); draw_ofs.y = vscroll->get_value(); - edit_draw->queue_redraw(); + + texture_preview->queue_redraw(); + texture_overlay->queue_redraw(); } void TextureRegionEditor::_set_snap_mode(int p_mode) { - snap_mode = p_mode; - - if (snap_mode == SNAP_GRID) { - hb_grid->show(); - } else { - hb_grid->hide(); - } + snap_mode = (SnapMode)p_mode; + hb_grid->set_visible(snap_mode == SNAP_GRID); if (snap_mode == SNAP_AUTOSLICE && is_visible() && autoslice_is_dirty) { _update_autoslice(); } - edit_draw->queue_redraw(); + texture_overlay->queue_redraw(); } void TextureRegionEditor::_set_snap_off_x(float p_val) { snap_offset.x = p_val; - edit_draw->queue_redraw(); + texture_overlay->queue_redraw(); } void TextureRegionEditor::_set_snap_off_y(float p_val) { snap_offset.y = p_val; - edit_draw->queue_redraw(); + texture_overlay->queue_redraw(); } void TextureRegionEditor::_set_snap_step_x(float p_val) { snap_step.x = p_val; - edit_draw->queue_redraw(); + texture_overlay->queue_redraw(); } void TextureRegionEditor::_set_snap_step_y(float p_val) { snap_step.y = p_val; - edit_draw->queue_redraw(); + texture_overlay->queue_redraw(); } void TextureRegionEditor::_set_snap_sep_x(float p_val) { snap_separation.x = p_val; - edit_draw->queue_redraw(); + texture_overlay->queue_redraw(); } void TextureRegionEditor::_set_snap_sep_y(float p_val) { snap_separation.y = p_val; - edit_draw->queue_redraw(); + texture_overlay->queue_redraw(); } void TextureRegionEditor::_zoom_on_position(float p_zoom, Point2 p_position) { @@ -696,79 +707,52 @@ void TextureRegionEditor::_zoom_on_position(float p_zoom, Point2 p_position) { ofs = ofs / prev_zoom - ofs / draw_zoom; draw_ofs = (draw_ofs + ofs).round(); - edit_draw->queue_redraw(); + texture_preview->queue_redraw(); + texture_overlay->queue_redraw(); } void TextureRegionEditor::_zoom_in() { - _zoom_on_position(draw_zoom * 1.5, edit_draw->get_size() / 2.0); + _zoom_on_position(draw_zoom * 1.5, texture_overlay->get_size() / 2.0); } void TextureRegionEditor::_zoom_reset() { - _zoom_on_position(1.0, edit_draw->get_size() / 2.0); + _zoom_on_position(1.0, texture_overlay->get_size() / 2.0); } void TextureRegionEditor::_zoom_out() { - _zoom_on_position(draw_zoom / 1.5, edit_draw->get_size() / 2.0); + _zoom_on_position(draw_zoom / 1.5, texture_overlay->get_size() / 2.0); } -void TextureRegionEditor::apply_rect(const Rect2 &p_rect) { - if (atlas_tex.is_valid()) { - atlas_tex->set_region(p_rect); - } else if (node_sprite_2d) { +void TextureRegionEditor::_apply_rect(const Rect2 &p_rect) { + if (node_sprite_2d) { node_sprite_2d->set_region_rect(p_rect); } else if (node_sprite_3d) { node_sprite_3d->set_region_rect(p_rect); } else if (node_ninepatch) { node_ninepatch->set_region_rect(p_rect); - } else if (obj_styleBox.is_valid()) { - obj_styleBox->set_region_rect(p_rect); + } else if (res_stylebox.is_valid()) { + res_stylebox->set_region_rect(p_rect); + } else if (res_atlas_texture.is_valid()) { + res_atlas_texture->set_region(p_rect); } } void TextureRegionEditor::_update_rect() { - if (atlas_tex.is_valid()) { - rect = atlas_tex->get_region(); - } else if (node_sprite_2d) { - rect = node_sprite_2d->get_region_rect(); - } else if (node_sprite_3d) { - rect = node_sprite_3d->get_region_rect(); - } else if (node_ninepatch) { - rect = node_ninepatch->get_region_rect(); - if (rect == Rect2()) { - rect = Rect2(Vector2(), node_ninepatch->get_texture()->get_size()); - } - } else if (obj_styleBox.is_valid()) { - rect = obj_styleBox->get_region_rect(); - if (rect == Rect2()) { - rect = Rect2(Vector2(), obj_styleBox->get_texture()->get_size()); - } - } + rect = _get_edited_object_region(); } void TextureRegionEditor::_update_autoslice() { autoslice_is_dirty = false; autoslice_cache.clear(); - Ref<Texture2D> texture = nullptr; - if (atlas_tex.is_valid()) { - texture = atlas_tex->get_atlas(); - } else if (node_sprite_2d) { - texture = node_sprite_2d->get_texture(); - } else if (node_sprite_3d) { - texture = node_sprite_3d->get_texture(); - } else if (node_ninepatch) { - texture = node_ninepatch->get_texture(); - } else if (obj_styleBox.is_valid()) { - texture = obj_styleBox->get_texture(); - } - - if (texture.is_null()) { + Ref<Texture2D> object_texture = _get_edited_object_texture(); + if (object_texture.is_null()) { return; } - for (int y = 0; y < texture->get_height(); y++) { - for (int x = 0; x < texture->get_width(); x++) { - if (texture->is_pixel_opaque(x, y)) { + for (int y = 0; y < object_texture->get_height(); y++) { + for (int x = 0; x < object_texture->get_width(); x++) { + if (object_texture->is_pixel_opaque(x, y)) { bool found = false; for (Rect2 &E : autoslice_cache) { Rect2 grown = E.grow(1.5); @@ -813,34 +797,37 @@ void TextureRegionEditor::_update_autoslice() { } } } - cache_map[texture->get_rid()] = autoslice_cache; + cache_map[object_texture->get_rid()] = autoslice_cache; } void TextureRegionEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_EXIT_TREE: { - get_tree()->disconnect("node_removed", callable_mp(this, &TextureRegionEditor::_node_removed)); + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning"))); } break; case NOTIFICATION_ENTER_TREE: { get_tree()->connect("node_removed", callable_mp(this, &TextureRegionEditor::_node_removed)); - [[fallthrough]]; - } - case NOTIFICATION_THEME_CHANGED: { - edit_draw->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); + + panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning"))); + + hb_grid->set_visible(snap_mode == SNAP_GRID); + if (snap_mode == SNAP_AUTOSLICE && is_visible() && autoslice_is_dirty) { + _update_autoslice(); + } } break; - case NOTIFICATION_READY: { + case NOTIFICATION_EXIT_TREE: { + get_tree()->disconnect("node_removed", callable_mp(this, &TextureRegionEditor::_node_removed)); + } break; + + case NOTIFICATION_THEME_CHANGED: { + texture_preview->add_theme_style_override("panel", get_theme_stylebox(SNAME("TextureRegionPreviewBG"), SNAME("EditorStyles"))); + texture_overlay->add_theme_style_override("panel", get_theme_stylebox(SNAME("TextureRegionPreviewFG"), SNAME("EditorStyles"))); + zoom_out->set_icon(get_theme_icon(SNAME("ZoomLess"), SNAME("EditorIcons"))); zoom_reset->set_icon(get_theme_icon(SNAME("ZoomReset"), SNAME("EditorIcons"))); zoom_in->set_icon(get_theme_icon(SNAME("ZoomMore"), SNAME("EditorIcons"))); - - vscroll->set_anchors_and_offsets_preset(Control::PRESET_RIGHT_WIDE); - hscroll->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_WIDE); - [[fallthrough]]; - } - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning"))); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -864,41 +851,19 @@ void TextureRegionEditor::_notification(int p_what) { } } -void TextureRegionEditor::_node_removed(Object *p_obj) { - if (p_obj == node_sprite_2d || p_obj == node_sprite_3d || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) { - node_sprite_2d = nullptr; - node_sprite_3d = nullptr; - node_ninepatch = nullptr; - obj_styleBox = Ref<StyleBox>(nullptr); - atlas_tex = Ref<AtlasTexture>(nullptr); +void TextureRegionEditor::_node_removed(Node *p_node) { + if (p_node == node_sprite_2d || p_node == node_sprite_3d || p_node == node_ninepatch) { + _clear_edited_object(); hide(); } } -void TextureRegionEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_edit_region"), &TextureRegionEditor::_edit_region); - ClassDB::bind_method(D_METHOD("_zoom_on_position"), &TextureRegionEditor::_zoom_on_position); - ClassDB::bind_method(D_METHOD("_update_rect"), &TextureRegionEditor::_update_rect); -} - -bool TextureRegionEditor::is_stylebox() { - return obj_styleBox.is_valid(); -} - -bool TextureRegionEditor::is_atlas_texture() { - return atlas_tex.is_valid(); -} - -bool TextureRegionEditor::is_ninepatch() { - return node_ninepatch != nullptr; -} - -Sprite2D *TextureRegionEditor::get_sprite_2d() { - return node_sprite_2d; -} - -Sprite3D *TextureRegionEditor::get_sprite_3d() { - return node_sprite_3d; +void TextureRegionEditor::_clear_edited_object() { + node_sprite_2d = nullptr; + node_sprite_3d = nullptr; + node_ninepatch = nullptr; + res_stylebox = Ref<StyleBoxTexture>(); + res_atlas_texture = Ref<AtlasTexture>(); } void TextureRegionEditor::edit(Object *p_obj) { @@ -911,18 +876,14 @@ void TextureRegionEditor::edit(Object *p_obj) { if (node_ninepatch) { node_ninepatch->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } - if (obj_styleBox.is_valid()) { - obj_styleBox->disconnect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed)); + if (res_stylebox.is_valid()) { + res_stylebox->disconnect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed)); } - if (atlas_tex.is_valid()) { - atlas_tex->disconnect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed)); + if (res_atlas_texture.is_valid()) { + res_atlas_texture->disconnect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed)); } - node_sprite_2d = nullptr; - node_sprite_3d = nullptr; - node_ninepatch = nullptr; - obj_styleBox = Ref<StyleBoxTexture>(nullptr); - atlas_tex = Ref<AtlasTexture>(nullptr); + _clear_edited_object(); if (p_obj) { node_sprite_2d = Object::cast_to<Sprite2D>(p_obj); @@ -931,11 +892,11 @@ void TextureRegionEditor::edit(Object *p_obj) { bool is_resource = false; if (Object::cast_to<StyleBoxTexture>(p_obj)) { - obj_styleBox = Ref<StyleBoxTexture>(Object::cast_to<StyleBoxTexture>(p_obj)); + res_stylebox = Ref<StyleBoxTexture>(p_obj); is_resource = true; } if (Object::cast_to<AtlasTexture>(p_obj)) { - atlas_tex = Ref<AtlasTexture>(Object::cast_to<AtlasTexture>(p_obj)); + res_atlas_texture = Ref<AtlasTexture>(p_obj); is_resource = true; } @@ -947,11 +908,54 @@ void TextureRegionEditor::edit(Object *p_obj) { _edit_region(); } - edit_draw->queue_redraw(); + texture_preview->queue_redraw(); + texture_overlay->queue_redraw(); popup_centered_ratio(0.5); request_center = true; } +Ref<Texture2D> TextureRegionEditor::_get_edited_object_texture() const { + if (node_sprite_2d) { + return node_sprite_2d->get_texture(); + } + if (node_sprite_3d) { + return node_sprite_3d->get_texture(); + } + if (node_ninepatch) { + return node_ninepatch->get_texture(); + } + if (res_stylebox.is_valid()) { + return res_stylebox->get_texture(); + } + if (res_atlas_texture.is_valid()) { + return res_atlas_texture->get_atlas(); + } + + return Ref<Texture2D>(); +} + +Rect2 TextureRegionEditor::_get_edited_object_region() const { + Rect2 region; + + if (node_sprite_2d) { + region = node_sprite_2d->get_region_rect(); + } else if (node_sprite_3d) { + region = node_sprite_3d->get_region_rect(); + } else if (node_ninepatch) { + region = node_ninepatch->get_region_rect(); + } else if (res_stylebox.is_valid()) { + region = res_stylebox->get_region_rect(); + } else if (res_atlas_texture.is_valid()) { + region = res_atlas_texture->get_region(); + } + + if (region == Rect2()) { + region = Rect2(Vector2(), _get_edited_object_texture()->get_size()); + } + + return region; +} + void TextureRegionEditor::_texture_changed() { if (!is_visible()) { return; @@ -960,17 +964,20 @@ void TextureRegionEditor::_texture_changed() { } void TextureRegionEditor::_edit_region() { - CanvasItem::TextureFilter filter = CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS; + Ref<Texture2D> object_texture = _get_edited_object_texture(); + if (object_texture.is_null()) { + _zoom_reset(); + hscroll->hide(); + vscroll->hide(); + texture_preview->queue_redraw(); + texture_overlay->queue_redraw(); + return; + } - Ref<Texture2D> texture = nullptr; - if (atlas_tex.is_valid()) { - texture = atlas_tex->get_atlas(); - } else if (node_sprite_2d) { - texture = node_sprite_2d->get_texture(); + CanvasItem::TextureFilter filter = CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS; + if (node_sprite_2d) { filter = node_sprite_2d->get_texture_filter_in_tree(); } else if (node_sprite_3d) { - texture = node_sprite_3d->get_texture(); - StandardMaterial3D::TextureFilter filter_3d = node_sprite_3d->get_texture_filter(); switch (filter_3d) { @@ -998,10 +1005,7 @@ void TextureRegionEditor::_edit_region() { break; } } else if (node_ninepatch) { - texture = node_ninepatch->get_texture(); filter = node_ninepatch->get_texture_filter_in_tree(); - } else if (obj_styleBox.is_valid()) { - texture = obj_styleBox->get_texture(); } // occurs when get_texture_filter_in_tree reaches the scene root @@ -1032,20 +1036,11 @@ void TextureRegionEditor::_edit_region() { } } - if (texture.is_null()) { - preview_tex->set_diffuse_texture(nullptr); - _zoom_reset(); - hscroll->hide(); - vscroll->hide(); - edit_draw->queue_redraw(); - return; - } - - preview_tex->set_texture_filter(filter); - preview_tex->set_diffuse_texture(texture); + texture_preview->set_texture_filter(filter); + texture_preview->set_texture_repeat(CanvasItem::TEXTURE_REPEAT_DISABLED); - if (cache_map.has(texture->get_rid())) { - autoslice_cache = cache_map[texture->get_rid()]; + if (cache_map.has(object_texture->get_rid())) { + autoslice_cache = cache_map[object_texture->get_rid()]; autoslice_is_dirty = false; } else { if (is_visible() && snap_mode == SNAP_AUTOSLICE) { @@ -1056,7 +1051,8 @@ void TextureRegionEditor::_edit_region() { } _update_rect(); - edit_draw->queue_redraw(); + texture_preview->queue_redraw(); + texture_overlay->queue_redraw(); } Vector2 TextureRegionEditor::snap_point(Vector2 p_target) const { @@ -1068,25 +1064,24 @@ Vector2 TextureRegionEditor::snap_point(Vector2 p_target) const { return p_target; } +void TextureRegionEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_rect"), &TextureRegionEditor::_update_rect); +} + TextureRegionEditor::TextureRegionEditor() { + set_title(TTR("Region Editor")); set_ok_button_text(TTR("Close")); - VBoxContainer *vb = memnew(VBoxContainer); - add_child(vb); - node_sprite_2d = nullptr; - node_sprite_3d = nullptr; - node_ninepatch = nullptr; - obj_styleBox = Ref<StyleBoxTexture>(nullptr); - atlas_tex = Ref<AtlasTexture>(nullptr); - - preview_tex = Ref<CanvasTexture>(memnew(CanvasTexture)); // A power-of-two value works better as a default grid size. snap_step = EditorSettings::get_singleton()->get_project_metadata("texture_region_editor", "snap_step", Vector2(8, 8)); snap_separation = EditorSettings::get_singleton()->get_project_metadata("texture_region_editor", "snap_separation", Vector2()); - snap_mode = EditorSettings::get_singleton()->get_project_metadata("texture_region_editor", "snap_mode", SNAP_NONE); - edited_margin = -1; - drag_index = -1; - drag = false; + snap_mode = (SnapMode)(int)EditorSettings::get_singleton()->get_project_metadata("texture_region_editor", "snap_mode", SNAP_NONE); + + panner.instantiate(); + panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); + + VBoxContainer *vb = memnew(VBoxContainer); + add_child(vb); HBoxContainer *hb_tools = memnew(HBoxContainer); vb->add_child(hb_tools); @@ -1169,22 +1164,21 @@ TextureRegionEditor::TextureRegionEditor() { hb_grid->hide(); - panner.instantiate(); - panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); - - edit_draw = memnew(Panel); - vb->add_child(edit_draw); - edit_draw->set_v_size_flags(Control::SIZE_EXPAND_FILL); - edit_draw->connect("draw", callable_mp(this, &TextureRegionEditor::_region_draw)); - edit_draw->connect("gui_input", callable_mp(this, &TextureRegionEditor::_region_input)); - edit_draw->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key)); - edit_draw->set_focus_mode(Control::FOCUS_CLICK); + texture_preview = memnew(PanelContainer); + vb->add_child(texture_preview); + texture_preview->set_v_size_flags(Control::SIZE_EXPAND_FILL); + texture_preview->set_clip_contents(true); + texture_preview->connect("draw", callable_mp(this, &TextureRegionEditor::_texture_preview_draw)); - draw_zoom = 1.0; - edit_draw->set_clip_contents(true); + texture_overlay = memnew(Panel); + texture_preview->add_child(texture_overlay); + texture_overlay->set_focus_mode(Control::FOCUS_CLICK); + texture_overlay->connect("draw", callable_mp(this, &TextureRegionEditor::_texture_overlay_draw)); + texture_overlay->connect("gui_input", callable_mp(this, &TextureRegionEditor::_texture_overlay_input)); + texture_overlay->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key)); HBoxContainer *zoom_hb = memnew(HBoxContainer); - edit_draw->add_child(zoom_hb); + texture_overlay->add_child(zoom_hb); zoom_hb->set_begin(Point2(5, 5)); zoom_out = memnew(Button); @@ -1206,18 +1200,16 @@ TextureRegionEditor::TextureRegionEditor() { zoom_hb->add_child(zoom_in); vscroll = memnew(VScrollBar); + vscroll->set_anchors_and_offsets_preset(Control::PRESET_RIGHT_WIDE); vscroll->set_step(0.001); - edit_draw->add_child(vscroll); vscroll->connect("value_changed", callable_mp(this, &TextureRegionEditor::_scroll_changed)); + texture_overlay->add_child(vscroll); + hscroll = memnew(HScrollBar); + hscroll->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_WIDE); hscroll->set_step(0.001); - edit_draw->add_child(hscroll); hscroll->connect("value_changed", callable_mp(this, &TextureRegionEditor::_scroll_changed)); - - updating_scroll = false; - autoslice_is_dirty = true; - - set_title(TTR("Region Editor")); + texture_overlay->add_child(hscroll); } //////////////////////// diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 6b7a198246..eeae9dc205 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -42,6 +42,7 @@ class AtlasTexture; class OptionButton; +class PanelContainer; class ViewPanner; class TextureRegionEditor : public AcceptDialog { @@ -66,16 +67,18 @@ class TextureRegionEditor : public AcceptDialog { SpinBox *sb_off_x = nullptr; SpinBox *sb_sep_y = nullptr; SpinBox *sb_sep_x = nullptr; - Panel *edit_draw = nullptr; + + PanelContainer *texture_preview = nullptr; + Panel *texture_overlay = nullptr; VScrollBar *vscroll = nullptr; HScrollBar *hscroll = nullptr; Vector2 draw_ofs; - float draw_zoom = 0.0; + float draw_zoom = 1.0; bool updating_scroll = false; - int snap_mode = 0; + SnapMode snap_mode = SNAP_NONE; Vector2 snap_offset; Vector2 snap_step; Vector2 snap_separation; @@ -83,28 +86,28 @@ class TextureRegionEditor : public AcceptDialog { Sprite2D *node_sprite_2d = nullptr; Sprite3D *node_sprite_3d = nullptr; NinePatchRect *node_ninepatch = nullptr; - Ref<StyleBoxTexture> obj_styleBox; - Ref<AtlasTexture> atlas_tex; - - Ref<CanvasTexture> preview_tex; + Ref<StyleBoxTexture> res_stylebox; + Ref<AtlasTexture> res_atlas_texture; Rect2 rect; Rect2 rect_prev; float prev_margin = 0.0f; - int edited_margin = 0; + int edited_margin = -1; HashMap<RID, List<Rect2>> cache_map; List<Rect2> autoslice_cache; - bool autoslice_is_dirty = false; + bool autoslice_is_dirty = true; bool drag = false; bool creating = false; Vector2 drag_from; - int drag_index = 0; + int drag_index = -1; bool request_center = false; Ref<ViewPanner> panner; void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); + void _scroll_changed(float); + Transform2D _get_offset_transform() const; void _set_snap_mode(int p_mode); void _set_snap_off_x(float p_val); @@ -113,35 +116,39 @@ class TextureRegionEditor : public AcceptDialog { void _set_snap_step_y(float p_val); void _set_snap_sep_x(float p_val); void _set_snap_sep_y(float p_val); + void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); void _zoom_in(); void _zoom_reset(); void _zoom_out(); - void apply_rect(const Rect2 &p_rect); + + void _apply_rect(const Rect2 &p_rect); void _update_rect(); void _update_autoslice(); + Ref<Texture2D> _get_edited_object_texture() const; + Rect2 _get_edited_object_region() const; void _texture_changed(); + void _node_removed(Node *p_node); + + void _edit_region(); + void _clear_edited_object(); + + void _draw_margin_line(Vector2 p_from, Vector2 p_to); protected: void _notification(int p_what); - void _node_removed(Object *p_obj); static void _bind_methods(); + void _texture_preview_draw(); + void _texture_overlay_draw(); + void _texture_overlay_input(const Ref<InputEvent> &p_input); + Vector2 snap_point(Vector2 p_target) const; public: - void _edit_region(); - void _region_draw(); - void _region_input(const Ref<InputEvent> &p_input); - void _scroll_changed(float); - bool is_stylebox(); - bool is_atlas_texture(); - bool is_ninepatch(); - Sprite2D *get_sprite_2d(); - Sprite3D *get_sprite_3d(); - void edit(Object *p_obj); + TextureRegionEditor(); }; diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp index 937480eb50..9387d53442 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.cpp +++ b/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -70,12 +70,17 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla Vector2i tile_id = atlas_source->get_tile_id(tile_index); atlas_size = atlas_size.max(tile_id + atlas_source->get_tile_size_in_atlas(tile_id)); - Rect2i new_tile_rect_in_altas = Rect2i(atlas_offset + tile_id, atlas_source->get_tile_size_in_atlas(tile_id)); + Rect2i new_tile_rect_in_atlas = Rect2i(atlas_offset + tile_id, atlas_source->get_tile_size_in_atlas(tile_id)); // Copy the texture. for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) { Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame); - Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size); + Vector2i new_position = new_tile_rect_in_atlas.position * new_texture_region_size; + if (frame > 0) { + new_position += src_rect.size * Vector2i(frame, 0); + atlas_size.x = MAX(frame + 1, atlas_size.x); + } + Rect2 dst_rect_wide = Rect2i(new_position, new_tile_rect_in_atlas.size * new_texture_region_size); if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) { output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height())); } @@ -83,7 +88,7 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla } // Add to the mapping. - merged_mapping[source_index][tile_id] = new_tile_rect_in_altas.position; + merged_mapping[source_index][tile_id] = new_tile_rect_in_atlas.position; } // Compute the atlas offset. @@ -110,6 +115,13 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla int changed_id = -1; if (alternative_id == 0) { merged->create_tile(tile_mapping.value, atlas_source->get_tile_size_in_atlas(tile_mapping.key)); + int count = atlas_source->get_tile_animation_frames_count(tile_mapping.key); + merged->set_tile_animation_frames_count(tile_mapping.value, count); + for (int i = 0; i < count; i++) { + merged->set_tile_animation_frame_duration(tile_mapping.value, i, atlas_source->get_tile_animation_frame_duration(tile_mapping.key, i)); + } + merged->set_tile_animation_speed(tile_mapping.value, atlas_source->get_tile_animation_speed(tile_mapping.key)); + merged->set_tile_animation_mode(tile_mapping.value, atlas_source->get_tile_animation_mode(tile_mapping.key)); } else { changed_id = merged->create_alternative_tile(tile_mapping.value, alternative_index); } diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 5815c85718..6e1841090b 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -4123,7 +4123,7 @@ TileMapEditor::TileMapEditor() { _tab_changed(0); // Registers UndoRedo inspector callback. - EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileMap"), callable_mp(this, &TileMapEditor::_move_tile_map_array_element)); + EditorNode::get_editor_data().add_move_array_element_function(SNAME("TileMap"), callable_mp(this, &TileMapEditor::_move_tile_map_array_element)); } TileMapEditor::~TileMapEditor() { diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index e7db13fae2..7262e9f5c3 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -111,7 +111,7 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list p_list->push_back(PropertyInfo(Variant::STRING, PNAME("name"))); p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("texture"), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D")); p_list->push_back(PropertyInfo(Variant::VECTOR2I, PNAME("margins"), PROPERTY_HINT_NONE, "suffix:px")); - p_list->push_back(PropertyInfo(Variant::VECTOR2I, PNAME("separation"))); + p_list->push_back(PropertyInfo(Variant::VECTOR2I, PNAME("separation"), PROPERTY_HINT_NONE, "suffix:px")); p_list->push_back(PropertyInfo(Variant::VECTOR2I, PNAME("texture_region_size"), PROPERTY_HINT_NONE, "suffix:px")); p_list->push_back(PropertyInfo(Variant::BOOL, PNAME("use_texture_padding"))); } @@ -990,7 +990,6 @@ void TileSetAtlasSourceEditor::_update_atlas_view() { // Create and position the button. Button *button = memnew(Button); - alternative_tiles_control->add_child(button); button->set_flat(true); button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); button->add_theme_style_override("normal", memnew(StyleBoxEmpty)); @@ -1000,6 +999,7 @@ void TileSetAtlasSourceEditor::_update_atlas_view() { button->connect("pressed", callable_mp(tile_set_atlas_source, &TileSetAtlasSource::create_alternative_tile).bind(tile_id, TileSetSource::INVALID_TILE_ALTERNATIVE)); button->set_rect(Rect2(Vector2(pos.x, pos.y + (y_increment - texture_region_base_size.y) / 2.0), Vector2(texture_region_base_size_min, texture_region_base_size_min))); button->set_expand_icon(true); + alternative_tiles_control->add_child(button); pos.y += y_increment; } @@ -2646,7 +2646,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control_unscaled, false); alternative_tiles_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); - EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetAtlasSourceEditor::_undo_redo_inspector_callback)); + EditorNode::get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetAtlasSourceEditor::_undo_redo_inspector_callback)); // -- Dialogs -- confirm_auto_create_tiles = memnew(AcceptDialog); diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 237330a285..eb645d1d3f 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -950,8 +950,8 @@ TileSetEditor::TileSetEditor() { expanded_area->hide(); // Registers UndoRedo inspector callback. - EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element)); - EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback)); + EditorNode::get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element)); + EditorNode::get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback)); } void TileSourceInspectorPlugin::_show_id_edit_dialog(Object *p_for_source) { diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 89f1f86461..a4ff9143f2 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -458,7 +458,7 @@ void VersionControlEditorPlugin::_force_push() { } void VersionControlEditorPlugin::_update_opened_tabs() { - Vector<EditorData::EditedScene> open_scenes = EditorNode::get_singleton()->get_editor_data().get_edited_scenes(); + Vector<EditorData::EditedScene> open_scenes = EditorNode::get_editor_data().get_edited_scenes(); for (int i = 0; i < open_scenes.size(); i++) { if (open_scenes[i].root == NULL) { continue; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 3062059001..330a9a36eb 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -3996,7 +3996,7 @@ void VisualShaderEditor::_show_members_dialog(bool at_mouse_pos, VisualShaderNod members_dialog->popup(); // Keep dialog within window bounds. - Rect2 window_rect = Rect2(DisplayServer::get_singleton()->window_get_position(), DisplayServer::get_singleton()->window_get_size()); + Rect2 window_rect = Rect2(get_window()->get_position(), get_window()->get_size()); Rect2 dialog_rect = Rect2(members_dialog->get_position(), members_dialog->get_size()); Vector2 difference = (dialog_rect.get_end() - window_rect.get_end()).max(Vector2()); members_dialog->set_position(members_dialog->get_position() - difference); @@ -6544,7 +6544,7 @@ void EditorPropertyVisualShaderMode::_option_selected(int p_which) { return; } - ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_singleton()->get_editor_data().get_editor("Shader")); + ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_editor_data().get_editor_by_name("Shader")); if (!shader_editor) { return; } diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 17826c8310..e33f9f921d 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -70,7 +70,6 @@ void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) { } void ProjectSettingsEditor::queue_save() { - EditorNode::get_singleton()->notify_settings_changed(); timer->start(); } @@ -734,7 +733,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { import_defaults_editor = memnew(ImportDefaultsEditor); import_defaults_editor->set_name(TTR("Import Defaults")); tab_container->add_child(import_defaults_editor); - import_defaults_editor->connect("project_settings_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); MovieWriter::set_extensions_hint(); // ensure extensions are properly displayed. } diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index 39affdd9f8..9d3cb4f2ae 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -444,11 +444,11 @@ String RenameDialog::_substitute(const String &subject, const Node *node, int co result = result.replace("${TYPE}", node->get_class()); } - int current = EditorNode::get_singleton()->get_editor_data().get_edited_scene(); + int current = EditorNode::get_editor_data().get_edited_scene(); // Always request the scene title with the extension stripped. // Otherwise, the result could vary depending on whether a scene with the same name // (but different extension) is currently open. - result = result.replace("${SCENE}", EditorNode::get_singleton()->get_editor_data().get_scene_title(current, true)); + result = result.replace("${SCENE}", EditorNode::get_editor_data().get_scene_title(current, true)); Node *root_node = SceneTree::get_singleton()->get_edited_scene_root(); if (root_node) { diff --git a/editor/scene_create_dialog.cpp b/editor/scene_create_dialog.cpp index 8f56267123..aac9ca3739 100644 --- a/editor/scene_create_dialog.cpp +++ b/editor/scene_create_dialog.cpp @@ -270,6 +270,7 @@ SceneCreateDialog::SceneCreateDialog() { root_name_edit = memnew(LineEdit); gc->add_child(root_name_edit); root_name_edit->set_tooltip_text(TTR("When empty, the root node name is derived from the scene name based on the \"editor/naming/node_name_casing\" project setting.")); + root_name_edit->set_auto_translate(false); root_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); root_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1)); } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 3096d20c19..b1e30d369f 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1296,14 +1296,14 @@ void SceneTreeDock::_notification(int p_what) { EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", callable_mp(this, &SceneTreeDock::_feature_profile_changed)); - CanvasItemEditorPlugin *canvas_item_plugin = Object::cast_to<CanvasItemEditorPlugin>(editor_data->get_editor("2D")); + CanvasItemEditorPlugin *canvas_item_plugin = Object::cast_to<CanvasItemEditorPlugin>(editor_data->get_editor_by_name("2D")); if (canvas_item_plugin) { canvas_item_plugin->get_canvas_item_editor()->connect("item_lock_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false)); canvas_item_plugin->get_canvas_item_editor()->connect("item_group_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false)); scene_tree->connect("node_changed", callable_mp((CanvasItem *)canvas_item_plugin->get_canvas_item_editor()->get_viewport_control(), &CanvasItem::queue_redraw)); } - Node3DEditorPlugin *spatial_editor_plugin = Object::cast_to<Node3DEditorPlugin>(editor_data->get_editor("3D")); + Node3DEditorPlugin *spatial_editor_plugin = Object::cast_to<Node3DEditorPlugin>(editor_data->get_editor_by_name("3D")); spatial_editor_plugin->get_spatial_editor()->connect("item_lock_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false)); spatial_editor_plugin->get_spatial_editor()->connect("item_group_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false)); @@ -3229,10 +3229,10 @@ void SceneTreeDock::_focus_node() { ERR_FAIL_COND(!node); if (node->is_class("CanvasItem")) { - CanvasItemEditorPlugin *editor = Object::cast_to<CanvasItemEditorPlugin>(editor_data->get_editor("2D")); + CanvasItemEditorPlugin *editor = Object::cast_to<CanvasItemEditorPlugin>(editor_data->get_editor_by_name("2D")); editor->get_canvas_item_editor()->focus_selection(); } else { - Node3DEditorPlugin *editor = Object::cast_to<Node3DEditorPlugin>(editor_data->get_editor("3D")); + Node3DEditorPlugin *editor = Object::cast_to<Node3DEditorPlugin>(editor_data->get_editor_by_name("3D")); editor->get_spatial_editor()->get_editor_viewport(0)->focus_selection(); } } diff --git a/modules/dds/image_loader_dds.cpp b/modules/dds/image_loader_dds.cpp index 42c8120595..d661f6664a 100644 --- a/modules/dds/image_loader_dds.cpp +++ b/modules/dds/image_loader_dds.cpp @@ -66,7 +66,6 @@ enum DDSFormat { DDS_BGR5A1, DDS_BGR565, DDS_BGR10A2, - DDS_INDEXED, DDS_LUMINANCE, DDS_LUMINANCE_ALPHA, DDS_MAX @@ -197,9 +196,9 @@ Error ImageLoaderDDS::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField dds_format = DDS_BGR10A2; } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) { dds_format = DDS_BGR565; - } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff) { + } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff) { dds_format = DDS_LUMINANCE; - } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff && format_alpha_mask == 0xff00) { + } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) { dds_format = DDS_LUMINANCE_ALPHA; } else if (format_flags & DDPF_INDEXED && format_rgb_bits == 8) { dds_format = DDS_BGR565; diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp index 26f326838c..1aecfc6de1 100644 --- a/modules/gdscript/editor/gdscript_docgen.cpp +++ b/modules/gdscript/editor/gdscript_docgen.cpp @@ -36,19 +36,19 @@ using GDP = GDScriptParser; using GDType = GDP::DataType; static String _get_script_path(const String &p_path) { - return vformat(R"("%s")", p_path.get_slice("://", 1)); + return p_path.trim_prefix("res://").quote(); } static String _get_class_name(const GDP::ClassNode &p_class) { const GDP::ClassNode *curr_class = &p_class; - if (!curr_class->identifier) { // All inner classes have a identifier, so this is the outer class + if (!curr_class->identifier) { // All inner classes have an identifier, so this is the outer class. return _get_script_path(curr_class->fqcn); } String full_name = curr_class->identifier->name; while (curr_class->outer) { curr_class = curr_class->outer; - if (!curr_class->identifier) { // All inner classes have a identifier, so this is the outer class + if (!curr_class->identifier) { // All inner classes have an identifier, so this is the outer class. return vformat("%s.%s", _get_script_path(curr_class->fqcn), full_name); } full_name = vformat("%s.%s", curr_class->identifier->name, full_name); @@ -56,20 +56,71 @@ static String _get_class_name(const GDP::ClassNode &p_class) { return full_name; } -static PropertyInfo _property_info_from_datatype(const GDType &p_type) { - PropertyInfo pi; - pi.type = p_type.builtin_type; - if (p_type.kind == GDType::CLASS) { - pi.class_name = _get_class_name(*p_type.class_type); - } else if (p_type.kind == GDType::ENUM && p_type.enum_type != StringName()) { - pi.type = Variant::INT; // Only int types are recognized as enums by the EditorHelp - pi.usage |= PROPERTY_USAGE_CLASS_IS_ENUM; - // Replace :: from enum's use of fully qualified class names with regular . - pi.class_name = String(p_type.native_type).replace("::", "."); - } else if (p_type.kind == GDType::NATIVE) { - pi.class_name = p_type.native_type; +static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return = false) { + if (!p_gdtype.is_hard_type()) { + r_type = "Variant"; + return; + } + switch (p_gdtype.kind) { + case GDType::BUILTIN: + if (p_gdtype.builtin_type == Variant::NIL) { + r_type = p_is_return ? "void" : "null"; + return; + } + if (p_gdtype.builtin_type == Variant::ARRAY && p_gdtype.has_container_element_type()) { + _doctype_from_gdtype(p_gdtype.get_container_element_type(), r_type, r_enum); + if (!r_enum.is_empty()) { + r_type = "int[]"; + r_enum += "[]"; + return; + } + if (!r_type.is_empty() && r_type != "Variant") { + r_type += "[]"; + return; + } + } + r_type = Variant::get_type_name(p_gdtype.builtin_type); + return; + case GDType::NATIVE: + r_type = p_gdtype.native_type; + return; + case GDType::SCRIPT: + if (p_gdtype.script_type.is_valid()) { + if (p_gdtype.script_type->get_global_name() != StringName()) { + r_type = _get_script_path(p_gdtype.script_type->get_global_name()); + return; + } + if (!p_gdtype.script_type->get_path().is_empty()) { + r_type = _get_script_path(p_gdtype.script_type->get_path()); + return; + } + } + if (!p_gdtype.script_path.is_empty()) { + r_type = _get_script_path(p_gdtype.script_path); + return; + } + r_type = "Object"; + return; + case GDType::CLASS: + r_type = _get_class_name(*p_gdtype.class_type); + return; + case GDType::ENUM: + r_type = "int"; + r_enum = String(p_gdtype.native_type).replace("::", "."); + if (r_enum.begins_with("res://")) { + r_enum = r_enum.trim_prefix("res://"); + int dot_pos = r_enum.rfind("."); + if (dot_pos >= 0) { + r_enum = r_enum.left(dot_pos).quote() + r_enum.substr(dot_pos); + } + } + return; + case GDType::VARIANT: + case GDType::RESOLVING: + case GDType::UNRESOLVED: + r_type = "Variant"; + return; } - return pi; } void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_class) { @@ -120,8 +171,8 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c p_script->member_lines[class_name] = inner_class->start_line; - // Recursively generate inner class docs - // Needs inner GDScripts to exist: previously generated in GDScriptCompiler::make_scripts() + // Recursively generate inner class docs. + // Needs inner GDScripts to exist: previously generated in GDScriptCompiler::make_scripts(). GDScriptDocGen::generate_docs(*p_script->subclasses[class_name], inner_class); } break; @@ -144,22 +195,33 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c p_script->member_lines[func_name] = m_func->start_line; - MethodInfo mi; - mi.name = func_name; + DocData::MethodDoc method_doc; + method_doc.name = func_name; + method_doc.description = m_func->doc_data.description; + method_doc.is_deprecated = m_func->doc_data.is_deprecated; + method_doc.is_experimental = m_func->doc_data.is_experimental; + method_doc.qualifiers = m_func->is_static ? "static" : ""; if (m_func->return_type) { - mi.return_val = _property_info_from_datatype(m_func->return_type->get_datatype()); + _doctype_from_gdtype(m_func->return_type->get_datatype(), method_doc.return_type, method_doc.return_enum, true); + } else { + method_doc.return_type = "Variant"; } + for (const GDScriptParser::ParameterNode *p : m_func->parameters) { - PropertyInfo pi = _property_info_from_datatype(p->get_datatype()); - pi.name = p->identifier->name; - mi.arguments.push_back(pi); + DocData::ArgumentDoc arg_doc; + arg_doc.name = p->identifier->name; + _doctype_from_gdtype(p->get_datatype(), arg_doc.type, arg_doc.enumeration); + if (p->initializer != nullptr) { + if (p->initializer->is_constant) { + arg_doc.default_value = p->initializer->reduced_value.get_construct_string().replace("\n", "\\n"); + } else { + arg_doc.default_value = "<unknown>"; + } + } + method_doc.arguments.push_back(arg_doc); } - DocData::MethodDoc method_doc; - DocData::method_doc_from_methodinfo(method_doc, mi, m_func->doc_data.description); - method_doc.is_deprecated = m_func->doc_data.is_deprecated; - method_doc.is_experimental = m_func->doc_data.is_experimental; doc.methods.push_back(method_doc); } break; @@ -169,18 +231,19 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c p_script->member_lines[signal_name] = m_signal->start_line; - MethodInfo mi; - mi.name = signal_name; - for (const GDScriptParser::ParameterNode *p : m_signal->parameters) { - PropertyInfo pi = _property_info_from_datatype(p->get_datatype()); - pi.name = p->identifier->name; - mi.arguments.push_back(pi); - } - DocData::MethodDoc signal_doc; - DocData::signal_doc_from_methodinfo(signal_doc, mi, m_signal->doc_data.description); + signal_doc.name = signal_name; + signal_doc.description = m_signal->doc_data.description; signal_doc.is_deprecated = m_signal->doc_data.is_deprecated; signal_doc.is_experimental = m_signal->doc_data.is_experimental; + + for (const GDScriptParser::ParameterNode *p : m_signal->parameters) { + DocData::ArgumentDoc arg_doc; + arg_doc.name = p->identifier->name; + _doctype_from_gdtype(p->get_datatype(), arg_doc.type, arg_doc.enumeration); + signal_doc.arguments.push_back(arg_doc); + } + doc.signals.push_back(signal_doc); } break; @@ -191,50 +254,41 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c p_script->member_lines[var_name] = m_var->start_line; DocData::PropertyDoc prop_doc; - prop_doc.name = var_name; prop_doc.description = m_var->doc_data.description; prop_doc.is_deprecated = m_var->doc_data.is_deprecated; prop_doc.is_experimental = m_var->doc_data.is_experimental; + _doctype_from_gdtype(m_var->get_datatype(), prop_doc.type, prop_doc.enumeration); - GDType dt = m_var->get_datatype(); - switch (dt.kind) { - case GDType::CLASS: - prop_doc.type = _get_class_name(*dt.class_type); + switch (m_var->property) { + case GDP::VariableNode::PROP_NONE: break; - case GDType::VARIANT: - prop_doc.type = "Variant"; + case GDP::VariableNode::PROP_INLINE: + if (m_var->setter != nullptr) { + prop_doc.setter = m_var->setter->identifier->name; + } + if (m_var->getter != nullptr) { + prop_doc.getter = m_var->getter->identifier->name; + } break; - case GDType::ENUM: - prop_doc.type = Variant::get_type_name(dt.builtin_type); - // Replace :: from enum's use of fully qualified class names with regular . - prop_doc.enumeration = String(dt.native_type).replace("::", "."); - break; - case GDType::NATIVE:; - prop_doc.type = dt.native_type; - break; - case GDType::BUILTIN: - prop_doc.type = Variant::get_type_name(dt.builtin_type); - break; - default: - // SCRIPT: can be preload()'d and perhaps used as types directly? - // RESOLVING & UNRESOLVED should never happen since docgen requires analyzing w/o errors + case GDP::VariableNode::PROP_SETGET: + if (m_var->setter_pointer != nullptr) { + prop_doc.setter = m_var->setter_pointer->name; + } + if (m_var->getter_pointer != nullptr) { + prop_doc.getter = m_var->getter_pointer->name; + } break; } - if (m_var->property == GDP::VariableNode::PROP_SETGET) { - if (m_var->setter_pointer != nullptr) { - prop_doc.setter = m_var->setter_pointer->name; - } - if (m_var->getter_pointer != nullptr) { - prop_doc.getter = m_var->getter_pointer->name; + if (m_var->initializer) { + if (m_var->initializer->is_constant) { + prop_doc.default_value = m_var->initializer->reduced_value.get_construct_string().replace("\n", "\\n"); + } else { + prop_doc.default_value = "<unknown>"; } } - if (m_var->initializer && m_var->initializer->is_constant) { - prop_doc.default_value = m_var->initializer->reduced_value.get_construct_string().replace("\n", ""); - } - prop_doc.overridden = false; doc.properties.push_back(prop_doc); @@ -280,8 +334,7 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c const_doc.is_experimental = m_enum_val.doc_data.is_experimental; doc.constants.push_back(const_doc); } break; - case GDP::ClassNode::Member::GROUP: - case GDP::ClassNode::Member::UNDEFINED: + default: break; } diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 862799a6ec..e488d6e266 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -304,7 +304,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l // Allow ABCDEF in hex notation. if (is_hex_notation && (is_hex_digit(str[j]) || is_a_digit)) { is_a_digit = true; - } else { + } else if (str[j] != '_') { is_hex_notation = false; } @@ -327,14 +327,14 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l } else if (!((str[j] == '-' || str[j] == '+') && str[j - 1] == 'e' && !prev_is_digit) && !(str[j] == '_' && (prev_is_digit || str[j - 1] == 'b' || str[j - 1] == 'x' || str[j - 1] == '.')) && !(str[j] == 'e' && (prev_is_digit || str[j - 1] == '_')) && - !(str[j] == '.' && (prev_is_digit || (!prev_is_binary_op && (j > 0 && (str[j - 1] == '-' || str[j - 1] == '+' || str[j - 1] == '~'))))) && + !(str[j] == '.' && (prev_is_digit || (!prev_is_binary_op && (j > 0 && (str[j - 1] == '_' || str[j - 1] == '-' || str[j - 1] == '+' || str[j - 1] == '~'))))) && !((str[j] == '-' || str[j] == '+' || str[j] == '~') && !is_binary_op && !prev_is_binary_op && str[j - 1] != 'e')) { /* This condition continues number highlighting in special cases. 1st row: '+' or '-' after scientific notation (like 3e-4); 2nd row: '_' as a numeric separator; 3rd row: Scientific notation 'e' and floating points; 4th row: Floating points inside the number, or leading if after a unary mathematical operator; - 5th row: Multiple unary mathematical operators (like ~-7)*/ + 5th row: Multiple unary mathematical operators (like ~-7) */ in_number = false; } } else if (str[j] == '.' && !is_binary_op && is_digit(str[j + 1]) && (j == 0 || (j > 0 && str[j - 1] != '.'))) { diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 9f9accf507..18c69467dc 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -613,144 +613,183 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type return result; } - StringName first = p_type->type_chain[0]->name; - - if (first == SNAME("Variant")) { - if (p_type->type_chain.size() == 2) { - // May be nested enum. - StringName enum_name = p_type->type_chain[1]->name; - StringName qualified_name = String(first) + ENUM_SEPARATOR + String(p_type->type_chain[1]->name); - if (CoreConstants::is_global_enum(qualified_name)) { - result = make_global_enum_type(enum_name, first, true); - return result; + const GDScriptParser::IdentifierNode *first_id = p_type->type_chain[0]; + StringName first = first_id->name; + bool type_found = false; + + if (first_id->suite && first_id->suite->has_local(first)) { + const GDScriptParser::SuiteNode::Local &local = first_id->suite->get_local(first); + if (local.type == GDScriptParser::SuiteNode::Local::CONSTANT) { + result = local.get_datatype(); + if (!result.is_set()) { + // Don't try to resolve it as the constant can be declared below. + push_error(vformat(R"(Local constant "%s" is not resolved at this point.)", first), first_id); + return bad_type; + } + if (result.is_meta_type) { + type_found = true; + } else if (Ref<Script>(local.constant->initializer->reduced_value).is_valid()) { + Ref<GDScript> gdscript = local.constant->initializer->reduced_value; + if (gdscript.is_valid()) { + Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path()); + if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { + push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), first_id); + return bad_type; + } + result = ref->get_parser()->head->get_datatype(); + } else { + result = make_script_meta_type(local.constant->initializer->reduced_value); + } + type_found = true; } else { - push_error(vformat(R"(Name "%s" is not a nested type of "Variant".)", enum_name), p_type->type_chain[1]); + push_error(vformat(R"(Local constant "%s" is not a valid type.)", first), first_id); return bad_type; } - } else if (p_type->type_chain.size() > 2) { - push_error(R"(Variant only contains enum types, which do not have nested types.)", p_type->type_chain[2]); - return bad_type; - } - result.kind = GDScriptParser::DataType::VARIANT; - } else if (first == SNAME("Object")) { - // Object is treated like a native type, not a built-in. - result.kind = GDScriptParser::DataType::NATIVE; - result.builtin_type = Variant::OBJECT; - result.native_type = SNAME("Object"); - } else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) { - // Built-in types. - if (p_type->type_chain.size() > 1) { - push_error(R"(Built-in types don't contain nested types.)", p_type->type_chain[1]); + } else { + push_error(vformat(R"(Local %s "%s" cannot be used as a type.)", local.get_name(), first), first_id); return bad_type; } - result.kind = GDScriptParser::DataType::BUILTIN; - result.builtin_type = GDScriptParser::get_builtin_type(first); + } - if (result.builtin_type == Variant::ARRAY) { - GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type)); - if (container_type.kind != GDScriptParser::DataType::VARIANT) { - container_type.is_constant = false; - result.set_container_element_type(container_type); - } - } - } else if (class_exists(first)) { - // Native engine classes. - result.kind = GDScriptParser::DataType::NATIVE; - result.builtin_type = Variant::OBJECT; - result.native_type = first; - } else if (ScriptServer::is_global_class(first)) { - if (parser->script_path == ScriptServer::get_global_class_path(first)) { - result = parser->head->get_datatype(); - } else { - String path = ScriptServer::get_global_class_path(first); - String ext = path.get_extension(); - if (ext == GDScriptLanguage::get_singleton()->get_extension()) { - Ref<GDScriptParserRef> ref = get_parser_for(path); - if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { - push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type); + if (!type_found) { + if (first == SNAME("Variant")) { + if (p_type->type_chain.size() == 2) { + // May be nested enum. + StringName enum_name = p_type->type_chain[1]->name; + StringName qualified_name = String(first) + ENUM_SEPARATOR + String(p_type->type_chain[1]->name); + if (CoreConstants::is_global_enum(qualified_name)) { + result = make_global_enum_type(enum_name, first, true); + return result; + } else { + push_error(vformat(R"(Name "%s" is not a nested type of "Variant".)", enum_name), p_type->type_chain[1]); return bad_type; } - result = ref->get_parser()->head->get_datatype(); - } else { - result = make_script_meta_type(ResourceLoader::load(path, "Script")); + } else if (p_type->type_chain.size() > 2) { + push_error(R"(Variant only contains enum types, which do not have nested types.)", p_type->type_chain[2]); + return bad_type; } - } - } else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) { - const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first); - Ref<GDScriptParserRef> ref = get_parser_for(autoload.path); - if (ref.is_null()) { - push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, autoload.path), p_type); - return bad_type; - } - if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { - push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type); - return bad_type; - } - result = ref->get_parser()->head->get_datatype(); - } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) { - // Native enum in current class. - result = make_native_enum_type(first, parser->current_class->base_type.native_type); - } else if (CoreConstants::is_global_enum(first)) { - if (p_type->type_chain.size() > 1) { - push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[1]); - return bad_type; - } - result = make_global_enum_type(first, StringName()); - } else { - // Classes in current scope. - List<GDScriptParser::ClassNode *> script_classes; - bool found = false; - get_class_node_current_scope_classes(parser->current_class, &script_classes); - for (GDScriptParser::ClassNode *script_class : script_classes) { - if (found) { - break; + result.kind = GDScriptParser::DataType::VARIANT; + } else if (first == SNAME("Object")) { + // Object is treated like a native type, not a built-in. + result.kind = GDScriptParser::DataType::NATIVE; + result.builtin_type = Variant::OBJECT; + result.native_type = SNAME("Object"); + } else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) { + // Built-in types. + if (p_type->type_chain.size() > 1) { + push_error(R"(Built-in types don't contain nested types.)", p_type->type_chain[1]); + return bad_type; } + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = GDScriptParser::get_builtin_type(first); - if (script_class->identifier && script_class->identifier->name == first) { - result = script_class->get_datatype(); - break; + if (result.builtin_type == Variant::ARRAY) { + GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type)); + if (container_type.kind != GDScriptParser::DataType::VARIANT) { + container_type.is_constant = false; + result.set_container_element_type(container_type); + } + } + } else if (class_exists(first)) { + // Native engine classes. + result.kind = GDScriptParser::DataType::NATIVE; + result.builtin_type = Variant::OBJECT; + result.native_type = first; + } else if (ScriptServer::is_global_class(first)) { + if (parser->script_path == ScriptServer::get_global_class_path(first)) { + result = parser->head->get_datatype(); + } else { + String path = ScriptServer::get_global_class_path(first); + String ext = path.get_extension(); + if (ext == GDScriptLanguage::get_singleton()->get_extension()) { + Ref<GDScriptParserRef> ref = get_parser_for(path); + if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { + push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type); + return bad_type; + } + result = ref->get_parser()->head->get_datatype(); + } else { + result = make_script_meta_type(ResourceLoader::load(path, "Script")); + } + } + } else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) { + const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first); + Ref<GDScriptParserRef> ref = get_parser_for(autoload.path); + if (ref.is_null()) { + push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, autoload.path), p_type); + return bad_type; + } + if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { + push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type); + return bad_type; } - if (script_class->members_indices.has(first)) { - resolve_class_member(script_class, first, p_type); + result = ref->get_parser()->head->get_datatype(); + } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) { + // Native enum in current class. + result = make_native_enum_type(first, parser->current_class->base_type.native_type); + } else if (CoreConstants::is_global_enum(first)) { + if (p_type->type_chain.size() > 1) { + push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[1]); + return bad_type; + } + result = make_global_enum_type(first, StringName()); + } else { + // Classes in current scope. + List<GDScriptParser::ClassNode *> script_classes; + bool found = false; + get_class_node_current_scope_classes(parser->current_class, &script_classes); + for (GDScriptParser::ClassNode *script_class : script_classes) { + if (found) { + break; + } - GDScriptParser::ClassNode::Member member = script_class->get_member(first); - switch (member.type) { - case GDScriptParser::ClassNode::Member::CLASS: - result = member.get_datatype(); - found = true; - break; - case GDScriptParser::ClassNode::Member::ENUM: - result = member.get_datatype(); - found = true; - break; - case GDScriptParser::ClassNode::Member::CONSTANT: - if (member.get_datatype().is_meta_type) { + if (script_class->identifier && script_class->identifier->name == first) { + result = script_class->get_datatype(); + break; + } + if (script_class->members_indices.has(first)) { + resolve_class_member(script_class, first, p_type); + + GDScriptParser::ClassNode::Member member = script_class->get_member(first); + switch (member.type) { + case GDScriptParser::ClassNode::Member::CLASS: result = member.get_datatype(); found = true; break; - } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) { - Ref<GDScript> gdscript = member.constant->initializer->reduced_value; - if (gdscript.is_valid()) { - Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path()); - if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { - push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type); - return bad_type; - } - result = ref->get_parser()->head->get_datatype(); - } else { - result = make_script_meta_type(member.constant->initializer->reduced_value); - } + case GDScriptParser::ClassNode::Member::ENUM: + result = member.get_datatype(); found = true; break; - } - [[fallthrough]]; - default: - push_error(vformat(R"("%s" is a %s but does not contain a type.)", first, member.get_type_name()), p_type); - return bad_type; + case GDScriptParser::ClassNode::Member::CONSTANT: + if (member.get_datatype().is_meta_type) { + result = member.get_datatype(); + found = true; + break; + } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) { + Ref<GDScript> gdscript = member.constant->initializer->reduced_value; + if (gdscript.is_valid()) { + Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path()); + if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { + push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type); + return bad_type; + } + result = ref->get_parser()->head->get_datatype(); + } else { + result = make_script_meta_type(member.constant->initializer->reduced_value); + } + found = true; + break; + } + [[fallthrough]]; + default: + push_error(vformat(R"("%s" is a %s but does not contain a type.)", first, member.get_type_name()), p_type); + return bad_type; + } } } } } + if (!result.is_set()) { push_error(vformat(R"(Could not find type "%s" in the current scope.)", first), p_type); return bad_type; @@ -882,9 +921,12 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, case GDScriptParser::ClassNode::Member::VARIABLE: { bool previous_static_context = static_context; static_context = member.variable->is_static; + check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable); + member.variable->set_datatype(resolving_datatype); resolve_variable(member.variable, false); + resolve_pending_lambda_bodies(); // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) { @@ -893,7 +935,9 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, E->apply(parser, member.variable); } } + static_context = previous_static_context; + #ifdef DEBUG_ENABLED if (member.variable->exported && member.variable->onready) { parser->push_warning(member.variable, GDScriptWarning::ONREADY_WITH_EXPORT); @@ -1345,6 +1389,11 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co } } + if (!pending_body_resolution_lambdas.is_empty()) { + ERR_PRINT("GDScript bug (please report): Not all pending lambda bodies were resolved in time."); + resolve_pending_lambda_bodies(); + } + parser->current_class = previous_class; } @@ -1757,6 +1806,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) { #endif // DEBUG_ENABLED resolve_node(stmt); + resolve_pending_lambda_bodies(); #ifdef DEBUG_ENABLED parser->ignored_warnings = previously_ignored_warnings; @@ -2001,13 +2051,16 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { } GDScriptParser::DataType variable_type; + String list_visible_type = "<unresolved type>"; if (list_resolved) { variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; variable_type.kind = GDScriptParser::DataType::BUILTIN; variable_type.builtin_type = Variant::INT; + list_visible_type = "Array[int]"; // NOTE: `range()` has `Array` return type. } else if (p_for->list) { resolve_node(p_for->list, false); GDScriptParser::DataType list_type = p_for->list->get_datatype(); + list_visible_type = list_type.to_string(); if (!list_type.is_hard_type()) { mark_node_unsafe(p_for->list); } @@ -2051,8 +2104,37 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list); } } + if (p_for->variable) { - p_for->variable->set_datatype(variable_type); + if (p_for->datatype_specifier) { + GDScriptParser::DataType specified_type = type_from_metatype(resolve_datatype(p_for->datatype_specifier)); + if (!specified_type.is_variant()) { + if (variable_type.is_variant() || !variable_type.is_hard_type()) { + mark_node_unsafe(p_for->variable); + p_for->use_conversion_assign = true; + } else if (!is_type_compatible(specified_type, variable_type, true, p_for->variable)) { + if (is_type_compatible(variable_type, specified_type)) { + mark_node_unsafe(p_for->variable); + p_for->use_conversion_assign = true; + } else { + push_error(vformat(R"(Unable to iterate on value of type "%s" with variable of type "%s".)", list_visible_type, specified_type.to_string()), p_for->datatype_specifier); + } + } else if (!is_type_compatible(specified_type, variable_type)) { + p_for->use_conversion_assign = true; +#ifdef DEBUG_ENABLED + } else { + parser->push_warning(p_for->datatype_specifier, GDScriptWarning::REDUNDANT_FOR_VARIABLE_TYPE, p_for->variable->name, variable_type.to_string(), specified_type.to_string()); +#endif + } +#ifdef DEBUG_ENABLED + } else { + parser->push_warning(p_for->datatype_specifier, GDScriptWarning::REDUNDANT_FOR_VARIABLE_TYPE, p_for->variable->name, variable_type.to_string(), specified_type.to_string()); +#endif + } + p_for->variable->set_datatype(specified_type); + } else { + p_for->variable->set_datatype(variable_type); + } } resolve_suite(p_for->loop); @@ -3048,7 +3130,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a base_type.is_meta_type = false; is_self = true; - if (p_call->callee == nullptr && !lambda_stack.is_empty()) { + if (p_call->callee == nullptr && current_lambda != nullptr) { push_error("Cannot use `super()` inside a lambda.", p_call); } } else if (callee_type == GDScriptParser::Node::IDENTIFIER) { @@ -3721,7 +3803,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } } - if (!lambda_stack.is_empty()) { + if (current_lambda != nullptr) { // If the identifier is a member variable (including the native class properties) or a signal, we consider the lambda to be using `self`, so we keep a reference to the current instance. if (source_is_variable || source_is_signal) { mark_lambda_use_self(); @@ -3733,7 +3815,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident return; } - GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function; + GDScriptParser::FunctionNode *function_test = current_lambda->function; // Make sure we aren't capturing variable in the same lambda. // This also add captures for nested lambdas. while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) { @@ -3888,34 +3970,12 @@ void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) { return; } - lambda_stack.push_back(p_lambda); + GDScriptParser::LambdaNode *previous_lambda = current_lambda; + current_lambda = p_lambda; resolve_function_signature(p_lambda->function, p_lambda, true); - resolve_function_body(p_lambda->function, true); - lambda_stack.pop_back(); - - int captures_amount = p_lambda->captures.size(); - if (captures_amount > 0) { - // Create space for lambda parameters. - // At the beginning to not mess with optional parameters. - int param_count = p_lambda->function->parameters.size(); - p_lambda->function->parameters.resize(param_count + captures_amount); - for (int i = param_count - 1; i >= 0; i--) { - p_lambda->function->parameters.write[i + captures_amount] = p_lambda->function->parameters[i]; - p_lambda->function->parameters_indices[p_lambda->function->parameters[i]->identifier->name] = i + captures_amount; - } - - // Add captures as extra parameters at the beginning. - for (int i = 0; i < p_lambda->captures.size(); i++) { - GDScriptParser::IdentifierNode *capture = p_lambda->captures[i]; - GDScriptParser::ParameterNode *capture_param = parser->alloc_node<GDScriptParser::ParameterNode>(); - capture_param->identifier = capture; - capture_param->usages = capture->usages; - capture_param->set_datatype(capture->get_datatype()); + current_lambda = previous_lambda; - p_lambda->function->parameters.write[i] = capture_param; - p_lambda->function->parameters_indices[capture->name] = i; - } - } + pending_body_resolution_lambdas.push_back(p_lambda); } void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) { @@ -5256,9 +5316,53 @@ void GDScriptAnalyzer::downgrade_node_type_source(GDScriptParser::Node *p_node) } void GDScriptAnalyzer::mark_lambda_use_self() { - for (GDScriptParser::LambdaNode *lambda : lambda_stack) { + GDScriptParser::LambdaNode *lambda = current_lambda; + while (lambda != nullptr) { lambda->use_self = true; + lambda = lambda->parent_lambda; + } +} + +void GDScriptAnalyzer::resolve_pending_lambda_bodies() { + if (pending_body_resolution_lambdas.is_empty()) { + return; + } + + GDScriptParser::LambdaNode *previous_lambda = current_lambda; + + List<GDScriptParser::LambdaNode *> lambdas = pending_body_resolution_lambdas; + pending_body_resolution_lambdas.clear(); + + for (GDScriptParser::LambdaNode *lambda : lambdas) { + current_lambda = lambda; + resolve_function_body(lambda->function, true); + + int captures_amount = lambda->captures.size(); + if (captures_amount > 0) { + // Create space for lambda parameters. + // At the beginning to not mess with optional parameters. + int param_count = lambda->function->parameters.size(); + lambda->function->parameters.resize(param_count + captures_amount); + for (int i = param_count - 1; i >= 0; i--) { + lambda->function->parameters.write[i + captures_amount] = lambda->function->parameters[i]; + lambda->function->parameters_indices[lambda->function->parameters[i]->identifier->name] = i + captures_amount; + } + + // Add captures as extra parameters at the beginning. + for (int i = 0; i < lambda->captures.size(); i++) { + GDScriptParser::IdentifierNode *capture = lambda->captures[i]; + GDScriptParser::ParameterNode *capture_param = parser->alloc_node<GDScriptParser::ParameterNode>(); + capture_param->identifier = capture; + capture_param->usages = capture->usages; + capture_param->set_datatype(capture->get_datatype()); + + lambda->function->parameters.write[i] = capture_param; + lambda->function->parameters_indices[capture->name] = i; + } + } } + + current_lambda = previous_lambda; } bool GDScriptAnalyzer::class_exists(const StringName &p_class) const { diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 5bc2c89a87..ec155706df 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -43,7 +43,8 @@ class GDScriptAnalyzer { HashMap<String, Ref<GDScriptParserRef>> depended_parsers; const GDScriptParser::EnumNode *current_enum = nullptr; - List<GDScriptParser::LambdaNode *> lambda_stack; + GDScriptParser::LambdaNode *current_lambda = nullptr; + List<GDScriptParser::LambdaNode *> pending_body_resolution_lambdas; bool static_context = false; // Tests for detecting invalid overloading of script members @@ -129,6 +130,7 @@ class GDScriptAnalyzer { void mark_node_unsafe(const GDScriptParser::Node *p_node); void downgrade_node_type_source(GDScriptParser::Node *p_node); void mark_lambda_use_self(); + void resolve_pending_lambda_bodies(); bool class_exists(const StringName &p_class) const; Ref<GDScriptParserRef> get_parser_for(const String &p_path); void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype); diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 6057a00f9b..af7862efc5 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -1494,19 +1494,16 @@ void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_typ for_container_variables.push_back(container); } -void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_variable, const Address &p_list) { +void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_list) { const Address &container = for_container_variables.back()->get(); // Assign container. append_opcode(GDScriptFunction::OPCODE_ASSIGN); append(container); append(p_list); - - for_iterator_variables.push_back(p_variable); } -void GDScriptByteCodeGenerator::write_for() { - const Address &iterator = for_iterator_variables.back()->get(); +void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_conversion) { const Address &counter = for_counter_variables.back()->get(); const Address &container = for_container_variables.back()->get(); @@ -1599,11 +1596,16 @@ void GDScriptByteCodeGenerator::write_for() { } } + Address temp; + if (p_use_conversion) { + temp = Address(Address::LOCAL_VARIABLE, add_local("@iterator_temp", GDScriptDataType())); + } + // Begin loop. append_opcode(begin_opcode); append(counter); append(container); - append(iterator); + append(p_use_conversion ? temp : p_variable); for_jmp_addrs.push_back(opcodes.size()); append(0); // End of loop address, will be patched. append_opcode(GDScriptFunction::OPCODE_JUMP); @@ -1615,9 +1617,17 @@ void GDScriptByteCodeGenerator::write_for() { append_opcode(iterate_opcode); append(counter); append(container); - append(iterator); + append(p_use_conversion ? temp : p_variable); for_jmp_addrs.push_back(opcodes.size()); append(0); // Jump destination, will be patched. + + if (p_use_conversion) { + write_assign_with_conversion(p_variable, temp); + const GDScriptDataType &type = p_variable.type; + if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { + write_assign_false(temp); // Can contain RefCounted, so clear it. + } + } } void GDScriptByteCodeGenerator::write_endfor() { @@ -1639,7 +1649,6 @@ void GDScriptByteCodeGenerator::write_endfor() { current_breaks_to_patch.pop_back(); // Pop state. - for_iterator_variables.pop_back(); for_counter_variables.pop_back(); for_container_variables.pop_back(); } diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index bbcd252b13..671dea5d6d 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -143,7 +143,6 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { // Lists since these can be nested. List<int> if_jmp_addrs; List<int> for_jmp_addrs; - List<Address> for_iterator_variables; List<Address> for_counter_variables; List<Address> for_container_variables; List<int> while_jmp_addrs; @@ -536,8 +535,8 @@ public: virtual void write_jump_if_shared(const Address &p_value) override; virtual void write_end_jump_if_shared() override; virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override; - virtual void write_for_assignment(const Address &p_variable, const Address &p_list) override; - virtual void write_for() override; + virtual void write_for_assignment(const Address &p_list) override; + virtual void write_for(const Address &p_variable, bool p_use_conversion) override; virtual void write_endfor() override; virtual void start_while_condition() override; virtual void write_while(const Address &p_condition) override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 9810f5395a..cf17353dec 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -145,8 +145,8 @@ public: virtual void write_jump_if_shared(const Address &p_value) = 0; virtual void write_end_jump_if_shared() = 0; virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0; - virtual void write_for_assignment(const Address &p_variable, const Address &p_list) = 0; - virtual void write_for() = 0; + virtual void write_for_assignment(const Address &p_list) = 0; + virtual void write_for(const Address &p_variable, bool p_use_conversion) = 0; virtual void write_endfor() = 0; virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation. virtual void write_while(const Address &p_condition) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 3366fa2eec..f964db231a 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1953,13 +1953,13 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui return err; } - gen->write_for_assignment(iterator, list); + gen->write_for_assignment(list); if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } - gen->write_for(); + gen->write_for(iterator, for_n->use_conversion_assign); err = _parse_block(codegen, for_n->loop); if (err) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index b76ceea11f..0f7166c101 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1850,7 +1850,18 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() { n_for->variable = parse_identifier(); } - consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "for" variable name.)"); + if (match(GDScriptTokenizer::Token::COLON)) { + n_for->datatype_specifier = parse_type(); + if (n_for->datatype_specifier == nullptr) { + push_error(R"(Expected type specifier after ":".)"); + } + } + + if (n_for->datatype_specifier == nullptr) { + consume(GDScriptTokenizer::Token::IN, R"(Expected "in" or ":" after "for" variable name.)"); + } else { + consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "for" variable type specifier.)"); + } n_for->list = parse_expression(false); @@ -2280,9 +2291,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode IdentifierNode *identifier = alloc_node<IdentifierNode>(); complete_extents(identifier); identifier->name = previous.get_identifier(); -#ifdef DEBUG_ENABLED identifier->suite = current_suite; -#endif if (current_suite != nullptr && current_suite->has_local(identifier->name)) { const SuiteNode::Local &declaration = current_suite->get_local(identifier->name); @@ -3140,6 +3149,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_previous_operand, bool p_can_assign) { LambdaNode *lambda = alloc_node<LambdaNode>(); lambda->parent_function = current_function; + lambda->parent_lambda = current_lambda; + FunctionNode *function = alloc_node<FunctionNode>(); function->source_lambda = lambda; @@ -3167,6 +3178,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p FunctionNode *previous_function = current_function; current_function = function; + LambdaNode *previous_lambda = current_lambda; + current_lambda = lambda; + SuiteNode *body = alloc_node<SuiteNode>(); body->parent_function = current_function; body->parent_block = current_suite; @@ -3204,6 +3218,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p } current_function = previous_function; + current_lambda = previous_lambda; in_lambda = previous_in_lambda; lambda->function = function; @@ -4272,7 +4287,7 @@ String GDScriptParser::SuiteNode::Local::get_name() const { case SuiteNode::Local::FOR_VARIABLE: return "for loop iterator"; case SuiteNode::Local::PATTERN_BIND: - return "pattern_bind"; + return "pattern bind"; case SuiteNode::Local::UNDEFINED: return "<undefined>"; default: diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 71660d8f60..d1ab5f92cc 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -814,6 +814,8 @@ public: struct ForNode : public Node { IdentifierNode *variable = nullptr; + TypeNode *datatype_specifier = nullptr; + bool use_conversion_assign = false; ExpressionNode *list = nullptr; SuiteNode *loop = nullptr; @@ -857,9 +859,7 @@ public: struct IdentifierNode : public ExpressionNode { StringName name; -#ifdef DEBUG_ENABLED SuiteNode *suite = nullptr; // The block in which the identifier is used. -#endif enum Source { UNDEFINED_SOURCE, @@ -906,6 +906,7 @@ public: struct LambdaNode : public ExpressionNode { FunctionNode *function = nullptr; FunctionNode *parent_function = nullptr; + LambdaNode *parent_lambda = nullptr; Vector<IdentifierNode *> captures; HashMap<StringName, int> captures_indices; bool use_self = false; @@ -1319,6 +1320,7 @@ private: ClassNode *current_class = nullptr; FunctionNode *current_function = nullptr; + LambdaNode *current_lambda = nullptr; SuiteNode *current_suite = nullptr; CompletionContext completion_context; diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 24aa793c47..4fec445995 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -113,6 +113,14 @@ String GDScriptWarning::get_message() const { return R"(The "@static_unload" annotation is redundant because the file does not have a class with static variables.)"; case REDUNDANT_AWAIT: return R"("await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.)"; + case REDUNDANT_FOR_VARIABLE_TYPE: + CHECK_SYMBOLS(3); + if (symbols[1] == symbols[2]) { + return vformat(R"(The for loop iterator "%s" already has inferred type "%s", the specified type is redundant.)", symbols[0], symbols[1]); + } else { + return vformat(R"(The for loop iterator "%s" has inferred type "%s" but its supertype "%s" is specified.)", symbols[0], symbols[1], symbols[2]); + } + break; case ASSERT_ALWAYS_TRUE: return "Assert statement is redundant because the expression is always true."; case ASSERT_ALWAYS_FALSE: @@ -209,6 +217,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "STATIC_CALLED_ON_INSTANCE", "REDUNDANT_STATIC_UNLOAD", "REDUNDANT_AWAIT", + "REDUNDANT_FOR_VARIABLE_TYPE", "ASSERT_ALWAYS_TRUE", "ASSERT_ALWAYS_FALSE", "INTEGER_DIVISION", diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index 8444d46a88..73e12eb20e 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -73,6 +73,7 @@ public: STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself. REDUNDANT_STATIC_UNLOAD, // The `@static_unload` annotation is used but the class does not have static data. REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine). + REDUNDANT_FOR_VARIABLE_TYPE, // The for variable type specifier is a supertype of the inferred type. ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true. ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false. INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded. @@ -120,6 +121,7 @@ public: WARN, // STATIC_CALLED_ON_INSTANCE WARN, // REDUNDANT_STATIC_UNLOAD WARN, // REDUNDANT_AWAIT + WARN, // REDUNDANT_FOR_VARIABLE_TYPE WARN, // ASSERT_ALWAYS_TRUE WARN, // ASSERT_ALWAYS_FALSE WARN, // INTEGER_DIVISION diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 062be0fe20..8c44483288 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -73,6 +73,7 @@ void GDScriptLanguageServer::_notification(int p_what) { } void GDScriptLanguageServer::thread_main(void *p_userdata) { + set_current_thread_safe_for_nodes(true); GDScriptLanguageServer *self = static_cast<GDScriptLanguageServer *>(p_userdata); while (self->thread_running) { // Poll 20 times per second diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.gd new file mode 100644 index 0000000000..7e3b6e3c39 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.gd @@ -0,0 +1,4 @@ +func test(): + var a: Array[Resource] = [] + for node: Node in a: + print(node) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.out new file mode 100644 index 0000000000..8f8a368f9a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Unable to iterate on value of type "Array[Resource]" with variable of type "Node". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_call_arg.gd b/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_call_arg.gd new file mode 100644 index 0000000000..4b72fb9daa --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_call_arg.gd @@ -0,0 +1,5 @@ +var f = (func (_a): return 0).call(x) +var x = f + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_call_arg.out b/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_call_arg.out new file mode 100644 index 0000000000..6bca25b330 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_call_arg.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not resolve member "f": Cyclic reference. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_param.gd b/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_param.gd new file mode 100644 index 0000000000..115e8be50a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_param.gd @@ -0,0 +1,5 @@ +var f = func (_a = x): return 0 +var x = f + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_param.out b/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_param.out new file mode 100644 index 0000000000..6bca25b330 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_cyclic_ref_param.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not resolve member "f": Cyclic reference. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_before_declared.gd b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_before_declared.gd new file mode 100644 index 0000000000..7cdc14685f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_before_declared.gd @@ -0,0 +1,5 @@ +enum MyEnum {} + +func test(): + var e: E + const E = MyEnum diff --git a/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_before_declared.out b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_before_declared.out new file mode 100644 index 0000000000..e1d5837f32 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_before_declared.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Local constant "E" is not resolved at this point. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_const.gd b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_const.gd new file mode 100644 index 0000000000..68cf5efd8b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_const.gd @@ -0,0 +1,5 @@ +enum MyEnum {} + +func test(): + var E = MyEnum + var e: E diff --git a/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_const.out b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_const.out new file mode 100644 index 0000000000..b1f4e7a2c8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_const.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Local variable "E" cannot be used as a type. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_type.gd new file mode 100644 index 0000000000..ac446183cb --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_type.gd @@ -0,0 +1,5 @@ +enum MyEnum {A} + +func test(): + const E = MyEnum.A + var e: E diff --git a/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_type.out b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_type.out new file mode 100644 index 0000000000..c3c2c8ca2f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/local_const_as_type_use_not_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Local constant "E" is not a valid type. diff --git a/modules/gdscript/tests/scripts/analyzer/features/lambda_cyclic_ref_body.gd b/modules/gdscript/tests/scripts/analyzer/features/lambda_cyclic_ref_body.gd new file mode 100644 index 0000000000..e2f41a652c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/lambda_cyclic_ref_body.gd @@ -0,0 +1,34 @@ +# GH-70592 + +var f: Callable = func (): + x = 2 + return 1 + +var x: int = f.call() + +var g: Array[Callable] = [ + func (): + y += 10 + return 1, + func (): + y += 20 + return 2, +] + +var y: int = g[0].call() + g[1].call() + +func test(): + print(x) + f.call() + print(x) + + print(y) + g[0].call() + g[1].call() + print(y) + + # This prevents memory leak in CI. TODO: Investigate it. + # Also you cannot run the `EditorScript` twice without the cleaning. Error: + # Condition "!p_keep_state && has_instances" is true. Returning: ERR_ALREADY_IN_USE + f = Callable() + g.clear() diff --git a/modules/gdscript/tests/scripts/analyzer/features/lambda_cyclic_ref_body.out b/modules/gdscript/tests/scripts/analyzer/features/lambda_cyclic_ref_body.out new file mode 100644 index 0000000000..6917fa7c2c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/lambda_cyclic_ref_body.out @@ -0,0 +1,5 @@ +GDTEST_OK +1 +2 +3 +33 diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_const_as_type.gd b/modules/gdscript/tests/scripts/analyzer/features/local_const_as_type.gd new file mode 100644 index 0000000000..90c7f893b1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/local_const_as_type.gd @@ -0,0 +1,41 @@ +class InnerClass: + enum InnerEnum {A = 2} + const INNER_CONST = "INNER_CONST" + +enum Enum {A = 1} + +const Other = preload("./local_const_as_type.notest.gd") + +func test(): + const IC = InnerClass + const IE = IC.InnerEnum + const E = Enum + # Doesn't work in CI, but works in the editor. Looks like an unrelated bug. TODO: Investigate it. + # Error: Invalid call. Nonexistent function 'new' in base 'GDScript'. + var a1: IC = null # IC.new() + var a2: IE = IE.A + var a3: IC.InnerEnum = IE.A + var a4: E = E.A + print(a1.INNER_CONST) + print(a2) + print(a3) + print(a4) + + const O = Other + const OV: Variant = Other # Removes metatype. + const OIC = O.InnerClass + const OIE = OIC.InnerEnum + const OE = O.Enum + var b: O = O.new() + @warning_ignore("unsafe_method_access") + var bv: OV = OV.new() + var b1: OIC = OIC.new() + var b2: OIE = OIE.A + var b3: O.InnerClass.InnerEnum = OIE.A + var b4: OE = OE.A + print(b.CONST) + print(bv.CONST) + print(b1.INNER_CONST) + print(b2) + print(b3) + print(b4) diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_const_as_type.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/local_const_as_type.notest.gd new file mode 100644 index 0000000000..f16cdc18d8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/local_const_as_type.notest.gd @@ -0,0 +1,6 @@ +class InnerClass: + enum InnerEnum {A = 20} + const INNER_CONST = "OTHER_INNER_CONST" + +enum Enum {A = 10} +const CONST = "OTHER_CONST" diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_const_as_type.out b/modules/gdscript/tests/scripts/analyzer/features/local_const_as_type.out new file mode 100644 index 0000000000..b00024de2c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/local_const_as_type.out @@ -0,0 +1,11 @@ +GDTEST_OK +INNER_CONST +2 +2 +1 +OTHER_CONST +OTHER_CONST +OTHER_INNER_CONST +20 +20 +10 diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.gd b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.gd new file mode 100644 index 0000000000..1b32491e48 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.gd @@ -0,0 +1,4 @@ +func test(): + var a: Array[Node] = [] + for node: Node in a: + print(node) diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.out b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.out new file mode 100644 index 0000000000..3b3fbd9bd1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 3 +>> REDUNDANT_FOR_VARIABLE_TYPE +>> The for loop iterator "node" already has inferred type "Node", the specified type is redundant. diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.gd b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.gd new file mode 100644 index 0000000000..fcbc13c53d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.gd @@ -0,0 +1,4 @@ +func test(): + var a: Array[Node2D] = [] + for node: Node in a: + print(node) diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.out b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.out new file mode 100644 index 0000000000..36d4a161d3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 3 +>> REDUNDANT_FOR_VARIABLE_TYPE +>> The for loop iterator "node" has inferred type "Node2D" but its supertype "Node" is specified. diff --git a/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.gd b/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.gd new file mode 100644 index 0000000000..cdc278ecf5 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.gd @@ -0,0 +1,4 @@ +func test(): + var a: Array = [Resource.new()] + for node: Node in a: + print(node) diff --git a/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.out b/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.out new file mode 100644 index 0000000000..b7ef07afb2 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/for_loop_iterator_type_not_match_specified.gd +>> 3 +>> Trying to assign value of type 'Resource' to a variable of type 'Node'. diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd new file mode 100644 index 0000000000..58b4df5a79 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd @@ -0,0 +1,34 @@ +func test(): + print("Test range.") + for e: float in range(2, 5): + var elem := e + prints(var_to_str(e), var_to_str(elem)) + + print("Test int.") + for e: float in 3: + var elem := e + prints(var_to_str(e), var_to_str(elem)) + + print("Test untyped int array.") + var a1 := [10, 20, 30] + for e: float in a1: + var elem := e + prints(var_to_str(e), var_to_str(elem)) + + print("Test typed int array.") + var a2: Array[int] = [10, 20, 30] + for e: float in a2: + var elem := e + prints(var_to_str(e), var_to_str(elem)) + + print("Test String-keys dictionary.") + var d1 := {a = 1, b = 2, c = 3} + for k: StringName in d1: + var key := k + prints(var_to_str(k), var_to_str(key)) + + print("Test RefCounted-keys dictionary.") + var d2 := {RefCounted.new(): 1, Resource.new(): 2, ConfigFile.new(): 3} + for k: RefCounted in d2: + var key := k + prints(k.get_class(), key.get_class()) diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out new file mode 100644 index 0000000000..f67f7d89d5 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out @@ -0,0 +1,25 @@ +GDTEST_OK +Test range. +2.0 2.0 +3.0 3.0 +4.0 4.0 +Test int. +0.0 0.0 +1.0 1.0 +2.0 2.0 +Test untyped int array. +10.0 10.0 +20.0 20.0 +30.0 30.0 +Test typed int array. +10.0 10.0 +20.0 20.0 +30.0 30.0 +Test String-keys dictionary. +&"a" &"a" +&"b" &"b" +&"c" &"c" +Test RefCounted-keys dictionary. +RefCounted RefCounted +Resource Resource +ConfigFile ConfigFile diff --git a/modules/gltf/extensions/gltf_document_extension_texture_ktx.cpp b/modules/gltf/extensions/gltf_document_extension_texture_ktx.cpp new file mode 100644 index 0000000000..ca61a24201 --- /dev/null +++ b/modules/gltf/extensions/gltf_document_extension_texture_ktx.cpp @@ -0,0 +1,66 @@ +/**************************************************************************/ +/* gltf_document_extension_texture_ktx.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "gltf_document_extension_texture_ktx.h" + +// Import process. +Error GLTFDocumentExtensionTextureKTX::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) { + if (!p_extensions.has("KHR_texture_basisu")) { + return ERR_SKIP; + } + return OK; +} + +Vector<String> GLTFDocumentExtensionTextureKTX::get_supported_extensions() { + Vector<String> ret; + ret.push_back("KHR_texture_basisu"); + return ret; +} + +Error GLTFDocumentExtensionTextureKTX::parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image) { + if (p_mime_type == "image/ktx2") { + return r_image->load_ktx_from_buffer(p_image_data); + } + return OK; +} + +Error GLTFDocumentExtensionTextureKTX::parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) { + if (!p_texture_json.has("extensions")) { + return OK; + } + const Dictionary &extensions = p_texture_json["extensions"]; + if (!extensions.has("KHR_texture_basisu")) { + return OK; + } + const Dictionary &texture_ktx = extensions["KHR_texture_basisu"]; + ERR_FAIL_COND_V(!texture_ktx.has("source"), ERR_PARSE_ERROR); + r_gltf_texture->set_src_image(texture_ktx["source"]); + return OK; +} diff --git a/modules/gltf/extensions/gltf_document_extension_texture_ktx.h b/modules/gltf/extensions/gltf_document_extension_texture_ktx.h new file mode 100644 index 0000000000..e4cb38a044 --- /dev/null +++ b/modules/gltf/extensions/gltf_document_extension_texture_ktx.h @@ -0,0 +1,47 @@ +/**************************************************************************/ +/* gltf_document_extension_texture_ktx.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 GLTF_DOCUMENT_EXTENSION_TEXTURE_KTX_H +#define GLTF_DOCUMENT_EXTENSION_TEXTURE_KTX_H + +#include "gltf_document_extension.h" + +class GLTFDocumentExtensionTextureKTX : public GLTFDocumentExtension { + GDCLASS(GLTFDocumentExtensionTextureKTX, GLTFDocumentExtension); + +public: + // Import process. + Error import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) override; + Vector<String> get_supported_extensions() override; + Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image) override; + Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) override; +}; + +#endif // GLTF_DOCUMENT_EXTENSION_TEXTURE_KTX_H diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index 1788ffac3a..fecea45fc9 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -31,6 +31,7 @@ #include "register_types.h" #include "extensions/gltf_document_extension_convert_importer_mesh.h" +#include "extensions/gltf_document_extension_texture_ktx.h" #include "extensions/gltf_document_extension_texture_webp.h" #include "extensions/gltf_spec_gloss.h" #include "extensions/physics/gltf_document_extension_physics.h" @@ -55,18 +56,7 @@ static void _editor_init() { // Blend to glTF importer. bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled"); - // Defined here because EditorSettings doesn't exist in `register_gltf_types` yet. - EDITOR_DEF_RST("filesystem/import/blender/rpc_port", 6011); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, - "filesystem/import/blender/rpc_port", PROPERTY_HINT_RANGE, "0,65535,1")); - - EDITOR_DEF_RST("filesystem/import/blender/rpc_server_uptime", 5); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, - "filesystem/import/blender/rpc_server_uptime", PROPERTY_HINT_RANGE, "0,300,1,or_greater,suffix:s")); - - String blender3_path = EDITOR_DEF_RST("filesystem/import/blender/blender3_path", ""); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, - "filesystem/import/blender/blender3_path", PROPERTY_HINT_GLOBAL_DIR)); + String blender3_path = EDITOR_GET("filesystem/import/blender/blender3_path"); if (blend_enabled) { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (blender3_path.is_empty()) { @@ -89,10 +79,6 @@ static void _editor_init() { // FBX to glTF importer. bool fbx_enabled = GLOBAL_GET("filesystem/import/fbx/enabled"); - // Defined here because EditorSettings doesn't exist in `register_gltf_types` yet. - String fbx2gltf_path = EDITOR_DEF_RST("filesystem/import/fbx/fbx2gltf_path", ""); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, - "filesystem/import/fbx/fbx2gltf_path", PROPERTY_HINT_GLOBAL_FILE)); if (fbx_enabled) { Ref<EditorSceneFormatImporterFBX> importer; importer.instantiate(); @@ -133,6 +119,7 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(GLTFTextureSampler); // Register GLTFDocumentExtension classes with GLTFDocument. GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionPhysics); + GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionTextureKTX); GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionTextureWebP); bool is_editor = ::Engine::get_singleton()->is_editor_hint(); if (!is_editor) { diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index f9c3ca476a..3094a7bf80 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -228,6 +228,11 @@ Emitted when [member cell_size] changes. </description> </signal> + <signal name="changed"> + <description> + Emitted when the [MeshLibrary] of this GridMap changes. + </description> + </signal> </signals> <constants> <constant name="INVALID_CELL_ITEM" value="-1"> diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index d7ecf4ef1a..0c9f8fb3e0 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -32,6 +32,7 @@ #ifdef TOOLS_ENABLED +#include "core/core_string_names.h" #include "core/input/input.h" #include "core/os/keyboard.h" #include "editor/editor_node.h" @@ -341,7 +342,6 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b if (selected_palette < 0 && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE) { return false; } - Ref<MeshLibrary> mesh_library = node->get_mesh_library(); if (mesh_library.is_null()) { return false; } @@ -866,10 +866,7 @@ void GridMapEditor::update_palette() { mesh_library_palette->set_fixed_icon_size(Size2(min_size, min_size)); mesh_library_palette->set_max_text_lines(2); - Ref<MeshLibrary> mesh_library = node->get_mesh_library(); - if (mesh_library.is_null()) { - last_mesh_library = nullptr; search_box->set_text(""); search_box->set_editable(false); info_message->show(); @@ -922,13 +919,39 @@ void GridMapEditor::update_palette() { item++; } +} + +void GridMapEditor::_update_mesh_library() { + ERR_FAIL_NULL(node); + + Ref<MeshLibrary> new_mesh_library = node->get_mesh_library(); + if (new_mesh_library != mesh_library) { + if (mesh_library.is_valid()) { + mesh_library->disconnect_changed(callable_mp(this, &GridMapEditor::update_palette)); + } + mesh_library = new_mesh_library; + } else { + return; + } - last_mesh_library = *mesh_library; + if (mesh_library.is_valid()) { + mesh_library->connect_changed(callable_mp(this, &GridMapEditor::update_palette)); + } + + update_palette(); + // Update the cursor and grid in case the library is changed or removed. + _update_cursor_instance(); + update_grid(); } void GridMapEditor::edit(GridMap *p_gridmap) { - if (node && node->is_connected("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids))) { - node->disconnect("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids)); + if (node) { + node->disconnect(SNAME("cell_size_changed"), callable_mp(this, &GridMapEditor::_draw_grids)); + node->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GridMapEditor::_update_mesh_library)); + if (mesh_library.is_valid()) { + mesh_library->disconnect_changed(callable_mp(this, &GridMapEditor::update_palette)); + mesh_library = Ref<MeshLibrary>(); + } } node = p_gridmap; @@ -961,7 +984,9 @@ void GridMapEditor::edit(GridMap *p_gridmap) { _draw_grids(node->get_cell_size()); update_grid(); - node->connect("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids)); + node->connect(SNAME("cell_size_changed"), callable_mp(this, &GridMapEditor::_draw_grids)); + node->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GridMapEditor::_update_mesh_library)); + _update_mesh_library(); } void GridMapEditor::update_grid() { @@ -1092,13 +1117,6 @@ void GridMapEditor::_notification(int p_what) { } grid_xform = xf; } - Ref<MeshLibrary> cgmt = node->get_mesh_library(); - if (cgmt.operator->() != last_mesh_library) { - update_palette(); - // Update the cursor and grid in case the library is changed or removed. - _update_cursor_instance(); - update_grid(); - } } break; case NOTIFICATION_THEME_CHANGED: { @@ -1178,8 +1196,8 @@ GridMapEditor::GridMapEditor() { ED_SHORTCUT("grid_map/cursor_back_rotate_z", TTR("Cursor Back Rotate Z"), KeyModifierMask::SHIFT + Key::D, true); ED_SHORTCUT("grid_map/cursor_clear_rotation", TTR("Cursor Clear Rotation"), Key::W, true); ED_SHORTCUT("grid_map/paste_selects", TTR("Paste Selects")); - ED_SHORTCUT("grid_map/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::CTRL + Key::C, true); - ED_SHORTCUT("grid_map/cut_selection", TTR("Cut Selection"), KeyModifierMask::CTRL + Key::X, true); + ED_SHORTCUT("grid_map/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::CTRL + Key::C); + ED_SHORTCUT("grid_map/cut_selection", TTR("Cut Selection"), KeyModifierMask::CTRL + Key::X); ED_SHORTCUT("grid_map/clear_selection", TTR("Clear Selection"), Key::KEY_DELETE); ED_SHORTCUT("grid_map/fill_selection", TTR("Fill Selection"), KeyModifierMask::CTRL + Key::F); diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h index fd9daa7c29..924e21aef5 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.h +++ b/modules/gridmap/editor/grid_map_editor_plugin.h @@ -92,7 +92,7 @@ class GridMapEditor : public VBoxContainer { List<SetItem> set_items; GridMap *node = nullptr; - MeshLibrary *last_mesh_library = nullptr; + Ref<MeshLibrary> mesh_library = nullptr; Transform3D grid_xform; Transform3D edit_grid_xform; @@ -191,6 +191,7 @@ class GridMapEditor : public VBoxContainer { void _configure(); void _menu_option(int); void update_palette(); + void _update_mesh_library(); void _set_display_mode(int p_mode); void _item_selected_cbk(int idx); void _update_cursor_transform(); diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index f1e2218434..34c53b5de5 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -30,6 +30,7 @@ #include "grid_map.h" +#include "core/core_string_names.h" #include "core/io/marshalls.h" #include "core/object/message_queue.h" #include "scene/3d/light_3d.h" @@ -1122,6 +1123,7 @@ void GridMap::_bind_methods() { BIND_CONSTANT(INVALID_CELL_ITEM); ADD_SIGNAL(MethodInfo("cell_size_changed", PropertyInfo(Variant::VECTOR3, "cell_size"))); + ADD_SIGNAL(MethodInfo(CoreStringNames::get_singleton()->changed)); } void GridMap::set_cell_scale(float p_scale) { diff --git a/modules/ktx/SCsub b/modules/ktx/SCsub new file mode 100644 index 0000000000..b160f7a287 --- /dev/null +++ b/modules/ktx/SCsub @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_ktx = env_modules.Clone() + +# libktx thirdparty source files + +thirdparty_obj = [] + +thirdparty_dir = "#thirdparty/libktx/" +thirdparty_sources = [ + "lib/basis_transcode.cpp", + "lib/checkheader.c", + "lib/filestream.c", + "lib/hashlist.c", + "lib/memstream.c", + "lib/swap.c", + "lib/texture.c", + "lib/texture1.c", + "lib/texture2.c", + "lib/dfdutils/createdfd.c", + "lib/dfdutils/colourspaces.c", + "lib/dfdutils/interpretdfd.c", + "lib/dfdutils/printdfd.c", + "lib/dfdutils/queries.c", + "lib/dfdutils/vk2dfd.c", +] +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_ktx.Prepend(CPPPATH=[thirdparty_dir + "include"]) +env_ktx.Prepend(CPPPATH=[thirdparty_dir + "utils"]) +env_ktx.Prepend(CPPPATH=[thirdparty_dir + "lib"]) +env_ktx.Prepend(CPPPATH=[thirdparty_dir + "other_include"]) +env_ktx.Prepend(CPPPATH=["#thirdparty/basis_universal"]) + +if env["vulkan"]: + env_ktx.Prepend(CPPPATH=["#thirdparty/vulkan/include"]) +else: + # Falls back on bundled `vkformat_enum.h`. + env_ktx.Append(CPPDEFINES=["LIBKTX"]) + +env_ktx.Append(CPPDEFINES=[("KHRONOS_STATIC", 1)]) + +env_thirdparty = env_ktx.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files +module_obj = [] + +env_ktx.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/ktx/config.py b/modules/ktx/config.py new file mode 100644 index 0000000000..4c8391ea2a --- /dev/null +++ b/modules/ktx/config.py @@ -0,0 +1,7 @@ +def can_build(env, platform): + env.module_add_dependencies("ktx", ["basis_universal"]) + return True + + +def configure(env): + pass diff --git a/modules/ktx/register_types.cpp b/modules/ktx/register_types.cpp new file mode 100644 index 0000000000..1d48e05a90 --- /dev/null +++ b/modules/ktx/register_types.cpp @@ -0,0 +1,53 @@ +/**************************************************************************/ +/* register_types.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "register_types.h" + +#include "texture_loader_ktx.h" + +static Ref<ResourceFormatKTX> resource_loader_ktx; + +void initialize_ktx_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + + resource_loader_ktx.instantiate(); + ResourceLoader::add_resource_format_loader(resource_loader_ktx); +} + +void uninitialize_ktx_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + + ResourceLoader::remove_resource_format_loader(resource_loader_ktx); + resource_loader_ktx.unref(); +} diff --git a/modules/ktx/register_types.h b/modules/ktx/register_types.h new file mode 100644 index 0000000000..a50dc48b40 --- /dev/null +++ b/modules/ktx/register_types.h @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* register_types.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 KTX_REGISTER_TYPES_H +#define KTX_REGISTER_TYPES_H + +#include "modules/register_module_types.h" + +void initialize_ktx_module(ModuleInitializationLevel p_level); +void uninitialize_ktx_module(ModuleInitializationLevel p_level); + +#endif // KTX_REGISTER_TYPES_H diff --git a/modules/ktx/texture_loader_ktx.cpp b/modules/ktx/texture_loader_ktx.cpp new file mode 100644 index 0000000000..155ed56bd0 --- /dev/null +++ b/modules/ktx/texture_loader_ktx.cpp @@ -0,0 +1,562 @@ +/**************************************************************************/ +/* texture_loader_ktx.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "texture_loader_ktx.h" + +#include "core/io/file_access.h" +#include "core/io/file_access_memory.h" +#include "scene/resources/image_texture.h" + +#include <ktx.h> +#include <vk_format.h> + +KTX_error_code ktx_read(ktxStream *stream, void *dst, const ktx_size_t count) { + Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address); + (*f)->get_buffer(reinterpret_cast<uint8_t *>(dst), count); + return KTX_SUCCESS; +} + +KTX_error_code ktx_skip(ktxStream *stream, const ktx_size_t count) { + Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address); + for (ktx_size_t i = 0; i < count; ++i) { + (*f)->get_8(); + } + return KTX_SUCCESS; +} + +KTX_error_code ktx_write(ktxStream *stream, const void *src, const ktx_size_t size, const ktx_size_t count) { + Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address); + (*f)->store_buffer(reinterpret_cast<const uint8_t *>(src), size * count); + return KTX_SUCCESS; +} + +KTX_error_code ktx_getpos(ktxStream *stream, ktx_off_t *const offset) { + Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address); + *offset = (*f)->get_position(); + return KTX_SUCCESS; +} + +KTX_error_code ktx_setpos(ktxStream *stream, const ktx_off_t offset) { + Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address); + (*f)->seek(offset); + return KTX_SUCCESS; +} + +KTX_error_code ktx_getsize(ktxStream *stream, ktx_size_t *const size) { + Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address); + *size = (*f)->get_length(); + return KTX_SUCCESS; +} + +void ktx_destruct(ktxStream *stream) { + (void)stream; +} + +static Ref<Image> load_from_file_access(Ref<FileAccess> f, Error *r_error) { + ktxStream ktx_stream; + ktx_stream.read = ktx_read; + ktx_stream.skip = ktx_skip; + ktx_stream.write = ktx_write; + ktx_stream.getpos = ktx_getpos; + ktx_stream.setpos = ktx_setpos; + ktx_stream.getsize = ktx_getsize; + ktx_stream.destruct = ktx_destruct; + ktx_stream.type = eStreamTypeCustom; + ktx_stream.data.custom_ptr.address = &f; + ktx_stream.data.custom_ptr.allocatorAddress = NULL; + ktx_stream.data.custom_ptr.size = 0; + ktx_stream.readpos = 0; + ktx_stream.closeOnDestruct = false; + ktxTexture *ktx_texture; + KTX_error_code result = ktxTexture_CreateFromStream(&ktx_stream, + KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, + &ktx_texture); + if (result != KTX_SUCCESS) { + ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid or unsupported KTX texture file."); + } + + if (ktx_texture->numDimensions != 2) { + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported non-2D KTX texture file."); + } + + if (ktx_texture->isCubemap || ktx_texture->numFaces != 1) { + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported cube map KTX texture file."); + } + + if (ktx_texture->isArray) { + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported array KTX texture file."); + } + + uint32_t width = ktx_texture->baseWidth; + uint32_t height = ktx_texture->baseHeight; + uint32_t mipmaps = ktx_texture->numLevels; + Image::Format format; + bool srgb = false; + + switch (ktx_texture->classId) { + case ktxTexture1_c: + switch (((ktxTexture1 *)ktx_texture)->glInternalformat) { + case GL_LUMINANCE: + format = Image::FORMAT_L8; + break; + case GL_LUMINANCE_ALPHA: + format = Image::FORMAT_LA8; + break; + case GL_SRGB8: + format = Image::FORMAT_RGB8; + srgb = true; + break; + case GL_SRGB8_ALPHA8: + format = Image::FORMAT_RGBA8; + srgb = true; + break; + case GL_R8: + case GL_R8UI: + format = Image::FORMAT_R8; + break; + case GL_RG8: + format = Image::FORMAT_RG8; + break; + case GL_RGB8: + format = Image::FORMAT_RGB8; + break; + case GL_RGBA8: + format = Image::FORMAT_RGBA8; + break; + case GL_RGBA4: + format = Image::FORMAT_RGBA4444; + break; + case GL_RGB565: + format = Image::FORMAT_RGB565; + break; + case GL_R32F: + format = Image::FORMAT_RF; + break; + case GL_RG32F: + format = Image::FORMAT_RGF; + break; + case GL_RGB32F: + format = Image::FORMAT_RGBF; + break; + case GL_RGBA32F: + format = Image::FORMAT_RGBAF; + break; + case GL_R16F: + format = Image::FORMAT_RH; + break; + case GL_RG16F: + format = Image::FORMAT_RGH; + break; + case GL_RGB16F: + format = Image::FORMAT_RGBH; + break; + case GL_RGBA16F: + format = Image::FORMAT_RGBAH; + break; + case GL_RGB9_E5: + format = Image::FORMAT_RGBE9995; + break; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + format = Image::FORMAT_DXT1; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + format = Image::FORMAT_DXT3; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + format = Image::FORMAT_DXT5; + break; + case GL_COMPRESSED_RED_RGTC1: + format = Image::FORMAT_RGTC_R; + break; + case GL_COMPRESSED_RG_RGTC2: + format = Image::FORMAT_RGTC_RG; + break; + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: + format = Image::FORMAT_BPTC_RGBFU; + break; + case GL_COMPRESSED_RGBA_BPTC_UNORM: + format = Image::FORMAT_BPTC_RGBA; + break; +#if 0 // TODO: ETC compression is bogus. + case GL_ETC1_RGB8_OES: + format = Image::FORMAT_ETC; + break; + case GL_COMPRESSED_R11_EAC: + format = Image::FORMAT_ETC2_R11; + break; + case GL_COMPRESSED_SIGNED_R11_EAC: + format = Image::FORMAT_ETC2_R11S; + break; + case GL_COMPRESSED_RG11_EAC: + format = Image::FORMAT_ETC2_RG11; + break; + case GL_COMPRESSED_SIGNED_RG11_EAC: + format = Image::FORMAT_ETC2_RG11S; + break; + case GL_COMPRESSED_RGB8_ETC2: + format = Image::FORMAT_ETC2_RGB8; + break; + case GL_COMPRESSED_RGBA8_ETC2_EAC: + format = Image::FORMAT_ETC2_RGBA8; + break; + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + format = Image::FORMAT_ETC2_RGB8A1; + break; +#endif + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: + format = Image::FORMAT_ASTC_4x4; + break; + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: + format = Image::FORMAT_ASTC_4x4_HDR; + break; + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: + format = Image::FORMAT_ASTC_8x8; + break; + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: + format = Image::FORMAT_ASTC_8x8_HDR; + break; + default: + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported format " + itos(((ktxTexture1 *)ktx_texture)->glInternalformat) + " of KTX1 texture file."); + } + break; + case ktxTexture2_c: { + ktxTexture2 *ktx_texture2 = reinterpret_cast<ktxTexture2 *>(ktx_texture); + if (ktx_texture2->vkFormat == VK_FORMAT_UNDEFINED) { + if (!ktxTexture2_NeedsTranscoding(ktx_texture2)) { + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid VK_FORMAT_UNDEFINED of KTX2 texture file."); + } + ktx_transcode_fmt_e ktxfmt; + switch (ktxTexture2_GetNumComponents(ktx_texture2)) { + case 1: { + if (ktxTexture2_GetOETF_e(ktx_texture2) == KHR_DF_TRANSFER_SRGB) { // TODO srgb native support + ktxfmt = KTX_TTF_RGBA32; + } else if (RS::get_singleton()->has_os_feature("rgtc")) { + ktxfmt = KTX_TTF_BC4_R; + } else { + ktxfmt = KTX_TTF_RGBA32; + } + break; + } + case 2: { + if (ktxTexture2_GetOETF_e(ktx_texture2) == KHR_DF_TRANSFER_SRGB) { // TODO srgb native support + ktxfmt = KTX_TTF_RGBA32; + } else if (RS::get_singleton()->has_os_feature("rgtc")) { + ktxfmt = KTX_TTF_BC5_RG; + } else { + ktxfmt = KTX_TTF_RGBA32; + } + break; + } + case 3: { + if (ktxTexture2_GetOETF_e(ktx_texture2) == KHR_DF_TRANSFER_SRGB) { // TODO: srgb native support + ktxfmt = KTX_TTF_RGBA32; + } else if (RS::get_singleton()->has_os_feature("bptc")) { + ktxfmt = KTX_TTF_BC7_RGBA; + } else if (RS::get_singleton()->has_os_feature("s3tc")) { + ktxfmt = KTX_TTF_BC1_RGB; + } else if (RS::get_singleton()->has_os_feature("etc")) { + ktxfmt = KTX_TTF_ETC1_RGB; + } else { + ktxfmt = KTX_TTF_RGBA32; + } + break; + } + case 4: { + if (ktxTexture2_GetOETF_e(ktx_texture2) == KHR_DF_TRANSFER_SRGB) { // TODO srgb native support + ktxfmt = KTX_TTF_RGBA32; + } else if (RS::get_singleton()->has_os_feature("astc")) { + ktxfmt = KTX_TTF_ASTC_4x4_RGBA; + } else if (RS::get_singleton()->has_os_feature("bptc")) { + ktxfmt = KTX_TTF_BC7_RGBA; + } else if (RS::get_singleton()->has_os_feature("s3tc")) { + ktxfmt = KTX_TTF_BC3_RGBA; + } else if (RS::get_singleton()->has_os_feature("etc2")) { + ktxfmt = KTX_TTF_ETC2_RGBA; + } else { + ktxfmt = KTX_TTF_RGBA32; + } + break; + } + default: { + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid components of KTX2 texture file."); + } + } + result = ktxTexture2_TranscodeBasis(ktx_texture2, ktxfmt, 0); + if (result != KTX_SUCCESS) { + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Failed to transcode KTX2 texture file."); + } + } + switch (ktx_texture2->vkFormat) { + case VK_FORMAT_R8_UNORM: + format = Image::FORMAT_L8; + break; + case VK_FORMAT_R8G8_UNORM: + format = Image::FORMAT_LA8; + break; + case VK_FORMAT_R8G8B8_SRGB: + format = Image::FORMAT_RGB8; + srgb = true; + break; + case VK_FORMAT_R8G8B8A8_SRGB: + format = Image::FORMAT_RGBA8; + srgb = true; + break; + case VK_FORMAT_R8_UINT: + format = Image::FORMAT_R8; + break; + case VK_FORMAT_R8G8_UINT: + format = Image::FORMAT_RG8; + break; + case VK_FORMAT_R8G8B8_UINT: + format = Image::FORMAT_RGB8; + break; + case VK_FORMAT_R8G8B8A8_UINT: + format = Image::FORMAT_RGBA8; + break; + case VK_FORMAT_R4G4B4A4_UNORM_PACK16: + format = Image::FORMAT_RGBA4444; + break; + case VK_FORMAT_R5G6B5_UNORM_PACK16: + format = Image::FORMAT_RGB565; + break; + case VK_FORMAT_R32_SFLOAT: + format = Image::FORMAT_RF; + break; + case VK_FORMAT_R32G32_SFLOAT: + format = Image::FORMAT_RGF; + break; + case VK_FORMAT_R32G32B32_SFLOAT: + format = Image::FORMAT_RGBF; + break; + case VK_FORMAT_R32G32B32A32_SFLOAT: + format = Image::FORMAT_RGBAF; + break; + case VK_FORMAT_R16_SFLOAT: + format = Image::FORMAT_RH; + break; + case VK_FORMAT_R16G16_SFLOAT: + format = Image::FORMAT_RGH; + break; + case VK_FORMAT_R16G16B16_SFLOAT: + format = Image::FORMAT_RGBH; + break; + case VK_FORMAT_R16G16B16A16_SFLOAT: + format = Image::FORMAT_RGBAH; + break; + case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: + format = Image::FORMAT_RGBE9995; + break; + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + format = Image::FORMAT_DXT1; + break; + case VK_FORMAT_BC2_UNORM_BLOCK: + format = Image::FORMAT_DXT3; + break; + case VK_FORMAT_BC3_UNORM_BLOCK: + format = Image::FORMAT_DXT5; + break; + case VK_FORMAT_BC4_UNORM_BLOCK: + format = Image::FORMAT_RGTC_R; + break; + case VK_FORMAT_BC5_UNORM_BLOCK: + format = Image::FORMAT_RGTC_RG; + break; + case VK_FORMAT_BC6H_UFLOAT_BLOCK: + format = Image::FORMAT_BPTC_RGBFU; + break; + case VK_FORMAT_BC6H_SFLOAT_BLOCK: + format = Image::FORMAT_BPTC_RGBF; + break; + case VK_FORMAT_BC7_UNORM_BLOCK: + format = Image::FORMAT_BPTC_RGBA; + break; +#if 0 // TODO: ETC compression is bogus. + case VK_FORMAT_EAC_R11_UNORM_BLOCK: + format = Image::FORMAT_ETC2_R11; + break; + case VK_FORMAT_EAC_R11_SNORM_BLOCK: + format = Image::FORMAT_ETC2_R11S; + break; + case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: + format = Image::FORMAT_ETC2_RG11; + break; + case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: + format = Image::FORMAT_ETC2_RG11S; + break; + case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + format = Image::FORMAT_ETC2_RGB8; + break; + case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + format = Image::FORMAT_ETC2_RGBA8; + break; + case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + format = Image::FORMAT_ETC2_RGB8A1; + break; +#endif + case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: + format = Image::FORMAT_ASTC_4x4; + break; + case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: + format = Image::FORMAT_ASTC_4x4_HDR; + break; + case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: + format = Image::FORMAT_ASTC_8x8; + break; + case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: + format = Image::FORMAT_ASTC_8x8_HDR; + break; + default: + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported format " + itos(((ktxTexture2 *)ktx_texture)->vkFormat) + " of KTX2 texture file."); + break; + } + break; + } + default: + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported version KTX texture file."); + break; + } + + Vector<uint8_t> src_data; + + // KTX use 4-bytes padding, don't use mipmaps if padding is effective + // TODO: unpad dynamically + int pixel_size = Image::get_format_pixel_size(format); + int pixel_rshift = Image::get_format_pixel_rshift(format); + int block = Image::get_format_block_size(format); + int minw, minh; + Image::get_format_min_pixel_size(format, minw, minh); + int w = width; + int h = height; + for (uint32_t i = 0; i < mipmaps; ++i) { + ktx_size_t mip_size = ktxTexture_GetImageSize(ktx_texture, i); + size_t bw = w % block != 0 ? w + (block - w % block) : w; + size_t bh = h % block != 0 ? h + (block - h % block) : h; + size_t s = bw * bh; + s *= pixel_size; + s >>= pixel_rshift; + if (mip_size != static_cast<ktx_size_t>(s)) { + if (!i) { + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported padded KTX texture file."); + } + mipmaps = 1; + break; + } + w = MAX(minw, w >> 1); + h = MAX(minh, h >> 1); + } + + for (uint32_t i = 0; i < mipmaps; ++i) { + ktx_size_t mip_size = ktxTexture_GetImageSize(ktx_texture, i); + ktx_size_t offset; + if (ktxTexture_GetImageOffset(ktx_texture, i, 0, 0, &offset) != KTX_SUCCESS) { + ktxTexture_Destroy(ktx_texture); + ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid KTX texture file."); + } + int prev_size = src_data.size(); + src_data.resize(prev_size + mip_size); + memcpy(src_data.ptrw() + prev_size, ktxTexture_GetData(ktx_texture) + offset, mip_size); + } + + Ref<Image> img = memnew(Image(width, height, mipmaps - 1, format, src_data)); + if (srgb) { + img->srgb_to_linear(); + } + + if (r_error) { + *r_error = OK; + } + + ktxTexture_Destroy(ktx_texture); + return img; +} + +Ref<Resource> ResourceFormatKTX::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + + Error err; + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); + if (f.is_null()) { + return Ref<Resource>(); + } + + Ref<FileAccess> fref(f); + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + + ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Unable to open KTX texture file '" + p_path + "'."); + Ref<Image> img = load_from_file_access(f, r_error); + Ref<ImageTexture> texture = ImageTexture::create_from_image(img); + return texture; +} + +static Ref<Image> _ktx_mem_loader_func(const uint8_t *p_ktx, int p_size) { + Ref<FileAccessMemory> f; + f.instantiate(); + f->open_custom(p_ktx, p_size); + Error err; + Ref<Image> img = load_from_file_access(f, &err); + ERR_FAIL_COND_V(err, Ref<Image>()); + return img; +} + +void ResourceFormatKTX::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("ktx"); + p_extensions->push_back("ktx2"); +} + +bool ResourceFormatKTX::handles_type(const String &p_type) const { + return ClassDB::is_parent_class(p_type, "Texture2D"); +} + +String ResourceFormatKTX::get_resource_type(const String &p_path) const { + if (p_path.get_extension().to_lower() == "ktx" || p_path.get_extension().to_lower() == "ktx2") { + return "ImageTexture"; + } + return ""; +} + +ResourceFormatKTX::ResourceFormatKTX() { + Image::_ktx_mem_loader_func = _ktx_mem_loader_func; +} diff --git a/modules/ktx/texture_loader_ktx.h b/modules/ktx/texture_loader_ktx.h new file mode 100644 index 0000000000..0ea676be6b --- /dev/null +++ b/modules/ktx/texture_loader_ktx.h @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* texture_loader_ktx.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 TEXTURE_LOADER_KTX_H +#define TEXTURE_LOADER_KTX_H + +#include "core/io/resource_loader.h" +#include "scene/resources/texture.h" + +class ResourceFormatKTX : public ResourceFormatLoader { +public: + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; + + virtual ~ResourceFormatKTX() {} + ResourceFormatKTX(); +}; + +#endif // TEXTURE_LOADER_KTX_H diff --git a/modules/multiplayer/scene_replication_config.cpp b/modules/multiplayer/scene_replication_config.cpp index af6af35219..db615f8c20 100644 --- a/modules/multiplayer/scene_replication_config.cpp +++ b/modules/multiplayer/scene_replication_config.cpp @@ -162,6 +162,7 @@ void SceneReplicationConfig::remove_property(const NodePath &p_path) { properties.erase(p_path); sync_props.erase(p_path); spawn_props.erase(p_path); + watch_props.clear(); } bool SceneReplicationConfig::has_property(const NodePath &p_path) const { diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp index 63abbf0d71..80beed807e 100644 --- a/modules/openxr/action_map/openxr_action_map.cpp +++ b/modules/openxr/action_map/openxr_action_map.cpp @@ -547,7 +547,7 @@ PackedStringArray OpenXRActionMap::get_top_level_paths(const Ref<OpenXRAction> p for (int i = 0; i < interaction_profiles.size(); i++) { Ref<OpenXRInteractionProfile> ip = interaction_profiles[i]; - const OpenXRInteractionProfileMetaData::InteractionProfile *profile = OpenXRInteractionProfileMetaData::get_singleton()->get_profile(ip->get_interaction_profile_path()); + const OpenXRInteractionProfileMetadata::InteractionProfile *profile = OpenXRInteractionProfileMetadata::get_singleton()->get_profile(ip->get_interaction_profile_path()); if (profile != nullptr) { for (int j = 0; j < ip->get_binding_count(); j++) { @@ -556,7 +556,7 @@ PackedStringArray OpenXRActionMap::get_top_level_paths(const Ref<OpenXRAction> p PackedStringArray paths = binding->get_paths(); for (int k = 0; k < paths.size(); k++) { - const OpenXRInteractionProfileMetaData::IOPath *io_path = profile->get_io_path(paths[k]); + const OpenXRInteractionProfileMetadata::IOPath *io_path = profile->get_io_path(paths[k]); if (io_path != nullptr) { String top_path = io_path->top_level_path; diff --git a/modules/openxr/action_map/openxr_interaction_profile.h b/modules/openxr/action_map/openxr_interaction_profile.h index 4a82785f14..479cc3c527 100644 --- a/modules/openxr/action_map/openxr_interaction_profile.h +++ b/modules/openxr/action_map/openxr_interaction_profile.h @@ -32,7 +32,7 @@ #define OPENXR_INTERACTION_PROFILE_H #include "openxr_action.h" -#include "openxr_interaction_profile_meta_data.h" +#include "openxr_interaction_profile_metadata.h" #include "core/io/resource.h" diff --git a/modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp b/modules/openxr/action_map/openxr_interaction_profile_metadata.cpp index 11449bdfb4..9f0198dfc4 100644 --- a/modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp +++ b/modules/openxr/action_map/openxr_interaction_profile_metadata.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* openxr_interaction_profile_meta_data.cpp */ +/* openxr_interaction_profile_metadata.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,30 +28,30 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "openxr_interaction_profile_meta_data.h" +#include "openxr_interaction_profile_metadata.h" #include "../openxr_api.h" -OpenXRInteractionProfileMetaData *OpenXRInteractionProfileMetaData::singleton = nullptr; +OpenXRInteractionProfileMetadata *OpenXRInteractionProfileMetadata::singleton = nullptr; -OpenXRInteractionProfileMetaData::OpenXRInteractionProfileMetaData() { +OpenXRInteractionProfileMetadata::OpenXRInteractionProfileMetadata() { singleton = this; _register_core_metadata(); OpenXRAPI::register_extension_metadata(); } -OpenXRInteractionProfileMetaData::~OpenXRInteractionProfileMetaData() { +OpenXRInteractionProfileMetadata::~OpenXRInteractionProfileMetadata() { singleton = nullptr; } -void OpenXRInteractionProfileMetaData::_bind_methods() { - ClassDB::bind_method(D_METHOD("register_top_level_path", "display_name", "openxr_path", "openxr_extension_name"), &OpenXRInteractionProfileMetaData::register_top_level_path); - ClassDB::bind_method(D_METHOD("register_interaction_profile", "display_name", "openxr_path", "openxr_extension_name"), &OpenXRInteractionProfileMetaData::register_interaction_profile); - ClassDB::bind_method(D_METHOD("register_io_path", "interaction_profile", "display_name", "toplevel_path", "openxr_path", "openxr_extension_name", "action_type"), &OpenXRInteractionProfileMetaData::register_io_path); +void OpenXRInteractionProfileMetadata::_bind_methods() { + ClassDB::bind_method(D_METHOD("register_top_level_path", "display_name", "openxr_path", "openxr_extension_name"), &OpenXRInteractionProfileMetadata::register_top_level_path); + ClassDB::bind_method(D_METHOD("register_interaction_profile", "display_name", "openxr_path", "openxr_extension_name"), &OpenXRInteractionProfileMetadata::register_interaction_profile); + ClassDB::bind_method(D_METHOD("register_io_path", "interaction_profile", "display_name", "toplevel_path", "openxr_path", "openxr_extension_name", "action_type"), &OpenXRInteractionProfileMetadata::register_io_path); } -void OpenXRInteractionProfileMetaData::register_top_level_path(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name) { +void OpenXRInteractionProfileMetadata::register_top_level_path(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name) { ERR_FAIL_COND_MSG(has_top_level_path(p_openxr_path), p_openxr_path + " had already been registered"); TopLevelPath new_toplevel_path = { @@ -63,7 +63,7 @@ void OpenXRInteractionProfileMetaData::register_top_level_path(const String &p_d top_level_paths.push_back(new_toplevel_path); } -void OpenXRInteractionProfileMetaData::register_interaction_profile(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name) { +void OpenXRInteractionProfileMetadata::register_interaction_profile(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name) { ERR_FAIL_COND_MSG(has_interaction_profile(p_openxr_path), p_openxr_path + " has already been registered"); InteractionProfile new_profile; @@ -74,7 +74,7 @@ void OpenXRInteractionProfileMetaData::register_interaction_profile(const String interaction_profiles.push_back(new_profile); } -void OpenXRInteractionProfileMetaData::register_io_path(const String &p_interaction_profile, const String &p_display_name, const String &p_toplevel_path, const String &p_openxr_path, const String &p_openxr_extension_name, OpenXRAction::ActionType p_action_type) { +void OpenXRInteractionProfileMetadata::register_io_path(const String &p_interaction_profile, const String &p_display_name, const String &p_toplevel_path, const String &p_openxr_path, const String &p_openxr_extension_name, OpenXRAction::ActionType p_action_type) { ERR_FAIL_COND_MSG(!has_interaction_profile(p_interaction_profile), "Unknown interaction profile " + p_interaction_profile); ERR_FAIL_COND_MSG(!has_top_level_path(p_toplevel_path), "Unknown top level path " + p_toplevel_path); @@ -95,7 +95,7 @@ void OpenXRInteractionProfileMetaData::register_io_path(const String &p_interact } } -bool OpenXRInteractionProfileMetaData::has_top_level_path(const String p_openxr_path) const { +bool OpenXRInteractionProfileMetadata::has_top_level_path(const String p_openxr_path) const { for (int i = 0; i < top_level_paths.size(); i++) { if (top_level_paths[i].openxr_path == p_openxr_path) { return true; @@ -105,7 +105,7 @@ bool OpenXRInteractionProfileMetaData::has_top_level_path(const String p_openxr_ return false; } -String OpenXRInteractionProfileMetaData::get_top_level_name(const String p_openxr_path) const { +String OpenXRInteractionProfileMetadata::get_top_level_name(const String p_openxr_path) const { for (int i = 0; i < top_level_paths.size(); i++) { if (top_level_paths[i].openxr_path == p_openxr_path) { return top_level_paths[i].display_name; @@ -115,7 +115,7 @@ String OpenXRInteractionProfileMetaData::get_top_level_name(const String p_openx return String(); } -String OpenXRInteractionProfileMetaData::get_top_level_extension(const String p_openxr_path) const { +String OpenXRInteractionProfileMetadata::get_top_level_extension(const String p_openxr_path) const { for (int i = 0; i < top_level_paths.size(); i++) { if (top_level_paths[i].openxr_path == p_openxr_path) { return top_level_paths[i].openxr_extension_name; @@ -125,7 +125,7 @@ String OpenXRInteractionProfileMetaData::get_top_level_extension(const String p_ return XR_PATH_UNSUPPORTED_NAME; } -bool OpenXRInteractionProfileMetaData::has_interaction_profile(const String p_openxr_path) const { +bool OpenXRInteractionProfileMetadata::has_interaction_profile(const String p_openxr_path) const { for (int i = 0; i < interaction_profiles.size(); i++) { if (interaction_profiles[i].openxr_path == p_openxr_path) { return true; @@ -135,7 +135,7 @@ bool OpenXRInteractionProfileMetaData::has_interaction_profile(const String p_op return false; } -String OpenXRInteractionProfileMetaData::get_interaction_profile_extension(const String p_openxr_path) const { +String OpenXRInteractionProfileMetadata::get_interaction_profile_extension(const String p_openxr_path) const { for (int i = 0; i < interaction_profiles.size(); i++) { if (interaction_profiles[i].openxr_path == p_openxr_path) { return interaction_profiles[i].openxr_extension_name; @@ -145,7 +145,7 @@ String OpenXRInteractionProfileMetaData::get_interaction_profile_extension(const return XR_PATH_UNSUPPORTED_NAME; } -const OpenXRInteractionProfileMetaData::InteractionProfile *OpenXRInteractionProfileMetaData::get_profile(const String p_openxr_path) const { +const OpenXRInteractionProfileMetadata::InteractionProfile *OpenXRInteractionProfileMetadata::get_profile(const String p_openxr_path) const { for (int i = 0; i < interaction_profiles.size(); i++) { if (interaction_profiles[i].openxr_path == p_openxr_path) { return &interaction_profiles[i]; @@ -155,7 +155,7 @@ const OpenXRInteractionProfileMetaData::InteractionProfile *OpenXRInteractionPro return nullptr; } -bool OpenXRInteractionProfileMetaData::InteractionProfile::has_io_path(const String p_io_path) const { +bool OpenXRInteractionProfileMetadata::InteractionProfile::has_io_path(const String p_io_path) const { for (int i = 0; i < io_paths.size(); i++) { if (io_paths[i].openxr_path == p_io_path) { return true; @@ -165,7 +165,7 @@ bool OpenXRInteractionProfileMetaData::InteractionProfile::has_io_path(const Str return false; } -const OpenXRInteractionProfileMetaData::IOPath *OpenXRInteractionProfileMetaData::InteractionProfile::get_io_path(const String p_io_path) const { +const OpenXRInteractionProfileMetadata::IOPath *OpenXRInteractionProfileMetadata::InteractionProfile::get_io_path(const String p_io_path) const { for (int i = 0; i < io_paths.size(); i++) { if (io_paths[i].openxr_path == p_io_path) { return &io_paths[i]; @@ -175,8 +175,8 @@ const OpenXRInteractionProfileMetaData::IOPath *OpenXRInteractionProfileMetaData return nullptr; } -const OpenXRInteractionProfileMetaData::IOPath *OpenXRInteractionProfileMetaData::get_io_path(const String p_interaction_profile, const String p_io_path) const { - const OpenXRInteractionProfileMetaData::InteractionProfile *profile = get_profile(p_interaction_profile); +const OpenXRInteractionProfileMetadata::IOPath *OpenXRInteractionProfileMetadata::get_io_path(const String p_interaction_profile, const String p_io_path) const { + const OpenXRInteractionProfileMetadata::InteractionProfile *profile = get_profile(p_interaction_profile); if (profile != nullptr) { return profile->get_io_path(p_io_path); } @@ -184,7 +184,7 @@ const OpenXRInteractionProfileMetaData::IOPath *OpenXRInteractionProfileMetaData return nullptr; } -PackedStringArray OpenXRInteractionProfileMetaData::get_interaction_profile_paths() const { +PackedStringArray OpenXRInteractionProfileMetadata::get_interaction_profile_paths() const { PackedStringArray arr; for (int i = 0; i < interaction_profiles.size(); i++) { @@ -194,7 +194,7 @@ PackedStringArray OpenXRInteractionProfileMetaData::get_interaction_profile_path return arr; } -void OpenXRInteractionProfileMetaData::_register_core_metadata() { +void OpenXRInteractionProfileMetadata::_register_core_metadata() { // Note, currently we add definitions that belong in extensions. // Extensions are registered when our OpenXRAPI is instantiated // however this does not happen in the editor. diff --git a/modules/openxr/action_map/openxr_interaction_profile_meta_data.h b/modules/openxr/action_map/openxr_interaction_profile_metadata.h index 3eab2139ff..91f97c73a6 100644 --- a/modules/openxr/action_map/openxr_interaction_profile_meta_data.h +++ b/modules/openxr/action_map/openxr_interaction_profile_metadata.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* openxr_interaction_profile_meta_data.h */ +/* openxr_interaction_profile_metadata.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef OPENXR_INTERACTION_PROFILE_META_DATA_H -#define OPENXR_INTERACTION_PROFILE_META_DATA_H +#ifndef OPENXR_INTERACTION_PROFILE_METADATA_H +#define OPENXR_INTERACTION_PROFILE_METADATA_H /////////////////////////////////////////////////////////////////////////// -// Stores available interaction profile meta data +// Stores available interaction profile metadata // // OpenXR defines and hardcodes all the supported input devices and their // paths as part of the OpenXR spec. When support for new devices is @@ -57,7 +57,9 @@ #define XR_PATH_UNSUPPORTED_NAME "unsupported" -class OpenXRInteractionProfileMetaData : public Object { +class OpenXRInteractionProfileMetadata : public Object { + GDCLASS(OpenXRInteractionProfileMetadata, Object); + public: struct TopLevelPath { String display_name; // User friendly display name (i.e. Left controller) @@ -84,7 +86,7 @@ public: }; private: - static OpenXRInteractionProfileMetaData *singleton; + static OpenXRInteractionProfileMetadata *singleton; Vector<TopLevelPath> top_level_paths; Vector<InteractionProfile> interaction_profiles; @@ -95,10 +97,10 @@ protected: static void _bind_methods(); public: - static OpenXRInteractionProfileMetaData *get_singleton() { return singleton; } + static OpenXRInteractionProfileMetadata *get_singleton() { return singleton; } - OpenXRInteractionProfileMetaData(); - ~OpenXRInteractionProfileMetaData(); + OpenXRInteractionProfileMetadata(); + ~OpenXRInteractionProfileMetadata(); void register_top_level_path(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name); bool has_top_level_path(const String p_openxr_path) const; @@ -115,4 +117,4 @@ public: const IOPath *get_io_path(const String p_interaction_profile, const String p_io_path) const; }; -#endif // OPENXR_INTERACTION_PROFILE_META_DATA_H +#endif // OPENXR_INTERACTION_PROFILE_METADATA_H diff --git a/modules/openxr/config.py b/modules/openxr/config.py index 8ed06a1606..92aaf06fce 100644 --- a/modules/openxr/config.py +++ b/modules/openxr/config.py @@ -16,7 +16,10 @@ def get_doc_classes(): "OpenXRAction", "OpenXRActionSet", "OpenXRActionMap", + "OpenXRAPIExtension", + "OpenXRExtensionWrapperExtension", "OpenXRInteractionProfile", + "OpenXRInteractionProfileMetadata", "OpenXRIPBinding", "OpenXRHand", ] diff --git a/doc/classes/OpenXRAPIExtension.xml b/modules/openxr/doc_classes/OpenXRAPIExtension.xml index 181da916d1..e795311651 100644 --- a/doc/classes/OpenXRAPIExtension.xml +++ b/modules/openxr/doc_classes/OpenXRAPIExtension.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRAPIExtension" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="OpenXRAPIExtension" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Makes the OpenXR API available for GDExtension. </brief_description> diff --git a/doc/classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml index 004d10e588..e09b58e5b4 100644 --- a/doc/classes/OpenXRExtensionWrapperExtension.xml +++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRExtensionWrapperExtension" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="OpenXRExtensionWrapperExtension" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Allows clients to implement OpenXR extensions with GDExtension. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRInteractionProfile.xml b/modules/openxr/doc_classes/OpenXRInteractionProfile.xml index a69bb875fa..ed5113f83c 100644 --- a/modules/openxr/doc_classes/OpenXRInteractionProfile.xml +++ b/modules/openxr/doc_classes/OpenXRInteractionProfile.xml @@ -4,7 +4,7 @@ Suggested bindings object for OpenXR. </brief_description> <description> - This object stores suggested bindings for an interaction profile. Interaction profiles define the meta data for a tracked XR device such as an XR controller. + This object stores suggested bindings for an interaction profile. Interaction profiles define the metadata for a tracked XR device such as an XR controller. For more information see the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles]interaction profiles info in the OpenXR specification[/url]. </description> <tutorials> diff --git a/modules/openxr/doc_classes/OpenXRInteractionProfileMetadata.xml b/modules/openxr/doc_classes/OpenXRInteractionProfileMetadata.xml new file mode 100644 index 0000000000..111f41293c --- /dev/null +++ b/modules/openxr/doc_classes/OpenXRInteractionProfileMetadata.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="OpenXRInteractionProfileMetadata" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + Meta class registering supported devices in OpenXR. + </brief_description> + <description> + This class allows OpenXR core and extensions to register metadata relating to supported interaction devices such as controllers, trackers, haptic devices, etc. It is primarily used by the action map editor and to sanitize any action map by removing extension-dependent entries when applicable. + </description> + <tutorials> + </tutorials> + <methods> + <method name="register_interaction_profile"> + <return type="void" /> + <param index="0" name="display_name" type="String" /> + <param index="1" name="openxr_path" type="String" /> + <param index="2" name="openxr_extension_name" type="String" /> + <description> + Registers an interaction profile using its OpenXR designation (e.g. [code]/interaction_profiles/khr/simple_controller[/code] is the profile for OpenXR's simple controller profile). + [param display_name] is the description shown to the user. [param openxr_path] is the interaction profile path being registered. [param openxr_extension_name] optionally restricts this profile to the given extension being enabled/available. If the extension is not available, the profile and all related entries used in an action map are filtered out. + </description> + </method> + <method name="register_io_path"> + <return type="void" /> + <param index="0" name="interaction_profile" type="String" /> + <param index="1" name="display_name" type="String" /> + <param index="2" name="toplevel_path" type="String" /> + <param index="3" name="openxr_path" type="String" /> + <param index="4" name="openxr_extension_name" type="String" /> + <param index="5" name="action_type" type="int" enum="OpenXRAction.ActionType" /> + <description> + Registers an input/output path for the given [param interaction_profile]. The profile should previously have been registered using [method register_interaction_profile]. [param display_name] is the description shown to the user. [param toplevel_path] specifies the bind path this input/output can be bound to (e.g. [code]/user/hand/left[/code] or [code]/user/hand/right[/code]). [param openxr_path] is the action input/output being registered (e.g. [code]/user/hand/left/input/aim/pose[/code]). [param openxr_extension_name] restricts this input/output to an enabled/available extension, this doesn't need to repeat the extension on the profile but relates to overlapping extension (e.g. [code]XR_EXT_palm_pose[/code] that introduces [code]…/input/palm_ext/pose[/code] input paths). [param action_type] defines the type of input or output provided by OpenXR. + </description> + </method> + <method name="register_top_level_path"> + <return type="void" /> + <param index="0" name="display_name" type="String" /> + <param index="1" name="openxr_path" type="String" /> + <param index="2" name="openxr_extension_name" type="String" /> + <description> + Registers a top level path to which profiles can be bound. For instance [code]/user/hand/left[/code] refers to the bind point for the player's left hand. Extensions can register additional top level paths, for instance a haptic vest extension might register [code]/user/body/vest[/code]. + [param display_name] is the name shown to the user. [param openxr_path] is the top level path being registered. [param openxr_extension_name] is optional and ensures the top level path is only used if the specified extension is available/enabled. + When a top level path ends up being bound by OpenXR, a [XRPositionalTracker] is instantiated to manage the state of the device. + </description> + </method> + </methods> +</class> diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.cpp b/modules/openxr/editor/openxr_interaction_profile_editor.cpp index 1f63399ee7..9998bb80e3 100644 --- a/modules/openxr/editor/openxr_interaction_profile_editor.cpp +++ b/modules/openxr/editor/openxr_interaction_profile_editor.cpp @@ -148,7 +148,7 @@ OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase(Ref<OpenX String profile_path = interaction_profile->get_interaction_profile_path(); String profile_name = profile_path; - profile_def = OpenXRInteractionProfileMetaData::get_singleton()->get_profile(profile_path); + profile_def = OpenXRInteractionProfileMetadata::get_singleton()->get_profile(profile_path); if (profile_def != nullptr) { profile_name = profile_def->display_name; } @@ -185,7 +185,7 @@ void OpenXRInteractionProfileEditor::_on_remove_pressed(const String p_action, c undo_redo->commit_action(true); } -void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetaData::IOPath *p_io_path) { +void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetadata::IOPath *p_io_path) { HBoxContainer *path_hb = memnew(HBoxContainer); path_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); p_container->add_child(path_hb); @@ -274,7 +274,7 @@ void OpenXRInteractionProfileEditor::_update_interaction_profile() { // Determine toplevel paths Vector<String> top_level_paths; for (int i = 0; i < profile_def->io_paths.size(); i++) { - const OpenXRInteractionProfileMetaData::IOPath *io_path = &profile_def->io_paths[i]; + const OpenXRInteractionProfileMetadata::IOPath *io_path = &profile_def->io_paths[i]; if (!top_level_paths.has(io_path->top_level_path)) { top_level_paths.push_back(io_path->top_level_path); @@ -291,11 +291,11 @@ void OpenXRInteractionProfileEditor::_update_interaction_profile() { panel->add_child(container); Label *label = memnew(Label); - label->set_text(OpenXRInteractionProfileMetaData::get_singleton()->get_top_level_name(top_level_paths[i])); + label->set_text(OpenXRInteractionProfileMetadata::get_singleton()->get_top_level_name(top_level_paths[i])); container->add_child(label); for (int j = 0; j < profile_def->io_paths.size(); j++) { - const OpenXRInteractionProfileMetaData::IOPath *io_path = &profile_def->io_paths[j]; + const OpenXRInteractionProfileMetadata::IOPath *io_path = &profile_def->io_paths[j]; if (io_path->top_level_path == top_level_paths[i]) { _add_io_path(container, io_path); } diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.h b/modules/openxr/editor/openxr_interaction_profile_editor.h index b9b3fbd81d..2ec72127cf 100644 --- a/modules/openxr/editor/openxr_interaction_profile_editor.h +++ b/modules/openxr/editor/openxr_interaction_profile_editor.h @@ -33,7 +33,7 @@ #include "../action_map/openxr_action_map.h" #include "../action_map/openxr_interaction_profile.h" -#include "../action_map/openxr_interaction_profile_meta_data.h" +#include "../action_map/openxr_interaction_profile_metadata.h" #include "openxr_select_action_dialog.h" #include "editor/editor_undo_redo_manager.h" @@ -52,7 +52,7 @@ protected: static void _bind_methods(); void _notification(int p_what); - const OpenXRInteractionProfileMetaData::InteractionProfile *profile_def = nullptr; + const OpenXRInteractionProfileMetadata::InteractionProfile *profile_def = nullptr; public: Ref<OpenXRInteractionProfile> get_interaction_profile() { return interaction_profile; } @@ -77,7 +77,7 @@ private: HBoxContainer *main_hb = nullptr; OpenXRSelectActionDialog *select_action_dialog = nullptr; - void _add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetaData::IOPath *p_io_path); + void _add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetadata::IOPath *p_io_path); public: void select_action_for(const String p_io_path); diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp index 9362c08174..08fcdaf771 100644 --- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp +++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp @@ -75,13 +75,13 @@ void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_inclu ip_buttons.clear(); // in with the new - PackedStringArray interaction_profiles = OpenXRInteractionProfileMetaData::get_singleton()->get_interaction_profile_paths(); + PackedStringArray interaction_profiles = OpenXRInteractionProfileMetadata::get_singleton()->get_interaction_profile_paths(); for (int i = 0; i < interaction_profiles.size(); i++) { String path = interaction_profiles[i]; if (!p_do_not_include.has(path)) { Button *ip_button = memnew(Button); ip_button->set_flat(true); - ip_button->set_text(OpenXRInteractionProfileMetaData::get_singleton()->get_profile(path)->display_name); + ip_button->set_text(OpenXRInteractionProfileMetadata::get_singleton()->get_profile(path)->display_name); ip_button->connect("pressed", callable_mp(this, &OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile).bind(path)); main_vb->add_child(ip_button); diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.h b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h index aae0368f67..1d1615321c 100644 --- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.h +++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h @@ -31,7 +31,7 @@ #ifndef OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H #define OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H -#include "../action_map/openxr_interaction_profile_meta_data.h" +#include "../action_map/openxr_interaction_profile_metadata.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" diff --git a/modules/openxr/extensions/openxr_htc_controller_extension.cpp b/modules/openxr/extensions/openxr_htc_controller_extension.cpp index 51364d83d8..c4ecb4e57c 100644 --- a/modules/openxr/extensions/openxr_htc_controller_extension.cpp +++ b/modules/openxr/extensions/openxr_htc_controller_extension.cpp @@ -30,7 +30,7 @@ #include "openxr_htc_controller_extension.h" -#include "../action_map/openxr_interaction_profile_meta_data.h" +#include "../action_map/openxr_interaction_profile_metadata.h" HashMap<String, bool *> OpenXRHTCControllerExtension::get_requested_extensions() { HashMap<String, bool *> request_extensions; @@ -46,7 +46,7 @@ bool OpenXRHTCControllerExtension::is_available(HTCControllers p_type) { } void OpenXRHTCControllerExtension::on_register_metadata() { - OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton(); + OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton(); ERR_FAIL_NULL(metadata); // HTC Vive Cosmos controller diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp index 087631a8b9..8b8c6c5353 100644 --- a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp +++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp @@ -30,7 +30,7 @@ #include "openxr_htc_vive_tracker_extension.h" -#include "../action_map/openxr_interaction_profile_meta_data.h" +#include "../action_map/openxr_interaction_profile_metadata.h" #include "core/string/print_string.h" @@ -47,7 +47,7 @@ bool OpenXRHTCViveTrackerExtension::is_available() { } void OpenXRHTCViveTrackerExtension::on_register_metadata() { - OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton(); + OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton(); ERR_FAIL_NULL(metadata); // HTC Vive tracker diff --git a/modules/openxr/extensions/openxr_huawei_controller_extension.cpp b/modules/openxr/extensions/openxr_huawei_controller_extension.cpp index 8b8662cfcb..baa6d6ded8 100644 --- a/modules/openxr/extensions/openxr_huawei_controller_extension.cpp +++ b/modules/openxr/extensions/openxr_huawei_controller_extension.cpp @@ -30,7 +30,7 @@ #include "openxr_huawei_controller_extension.h" -#include "../action_map/openxr_interaction_profile_meta_data.h" +#include "../action_map/openxr_interaction_profile_metadata.h" HashMap<String, bool *> OpenXRHuaweiControllerExtension::get_requested_extensions() { HashMap<String, bool *> request_extensions; @@ -45,7 +45,7 @@ bool OpenXRHuaweiControllerExtension::is_available() { } void OpenXRHuaweiControllerExtension::on_register_metadata() { - OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton(); + OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton(); ERR_FAIL_NULL(metadata); // Huawei controller diff --git a/modules/openxr/extensions/openxr_ml2_controller_extension.cpp b/modules/openxr/extensions/openxr_ml2_controller_extension.cpp index 7dad8deb8c..979ac22d08 100644 --- a/modules/openxr/extensions/openxr_ml2_controller_extension.cpp +++ b/modules/openxr/extensions/openxr_ml2_controller_extension.cpp @@ -30,7 +30,7 @@ #include "openxr_ml2_controller_extension.h" -#include "../action_map/openxr_interaction_profile_meta_data.h" +#include "../action_map/openxr_interaction_profile_metadata.h" HashMap<String, bool *> OpenXRML2ControllerExtension::get_requested_extensions() { HashMap<String, bool *> request_extensions; @@ -45,7 +45,7 @@ bool OpenXRML2ControllerExtension::is_available() { } void OpenXRML2ControllerExtension::on_register_metadata() { - OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton(); + OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton(); ERR_FAIL_NULL(metadata); // Magic Leap 2 Controller diff --git a/modules/openxr/extensions/openxr_pico_controller_extension.cpp b/modules/openxr/extensions/openxr_pico_controller_extension.cpp index 8be961a68e..6c492d2e5a 100644 --- a/modules/openxr/extensions/openxr_pico_controller_extension.cpp +++ b/modules/openxr/extensions/openxr_pico_controller_extension.cpp @@ -30,7 +30,7 @@ #include "openxr_pico_controller_extension.h" -#include "../action_map/openxr_interaction_profile_meta_data.h" +#include "../action_map/openxr_interaction_profile_metadata.h" // Pico controllers are not part of the OpenXR spec at the time of writing this // code. We'll hardcode the extension name that is used internally, verified by @@ -53,7 +53,7 @@ bool OpenXRPicoControllerExtension::is_available() { } void OpenXRPicoControllerExtension::on_register_metadata() { - OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton(); + OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton(); ERR_FAIL_NULL(metadata); // Pico controller (Pico 4 and Pico Neo 3 controllers) diff --git a/modules/openxr/extensions/openxr_wmr_controller_extension.cpp b/modules/openxr/extensions/openxr_wmr_controller_extension.cpp index a9b27013f1..7d43d2e304 100644 --- a/modules/openxr/extensions/openxr_wmr_controller_extension.cpp +++ b/modules/openxr/extensions/openxr_wmr_controller_extension.cpp @@ -30,7 +30,7 @@ #include "openxr_wmr_controller_extension.h" -#include "../action_map/openxr_interaction_profile_meta_data.h" +#include "../action_map/openxr_interaction_profile_metadata.h" HashMap<String, bool *> OpenXRWMRControllerExtension::get_requested_extensions() { HashMap<String, bool *> request_extensions; @@ -47,7 +47,7 @@ bool OpenXRWMRControllerExtension::is_available(WMRControllers p_type) { } void OpenXRWMRControllerExtension::on_register_metadata() { - OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton(); + OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton(); ERR_FAIL_NULL(metadata); // HP MR controller (newer G2 controllers) diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 2bd5376b21..17f1c3940c 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -216,7 +216,7 @@ bool OpenXRAPI::is_extension_enabled(const String &p_extension) const { } bool OpenXRAPI::is_top_level_path_supported(const String &p_toplevel_path) { - String required_extension = OpenXRInteractionProfileMetaData::get_singleton()->get_top_level_extension(p_toplevel_path); + String required_extension = OpenXRInteractionProfileMetadata::get_singleton()->get_top_level_extension(p_toplevel_path); // If unsupported is returned we likely have a misspelled interaction profile path in our action map. Always output that as an error. ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported toplevel path " + p_toplevel_path); @@ -236,7 +236,7 @@ bool OpenXRAPI::is_top_level_path_supported(const String &p_toplevel_path) { } bool OpenXRAPI::is_interaction_profile_supported(const String &p_ip_path) { - String required_extension = OpenXRInteractionProfileMetaData::get_singleton()->get_interaction_profile_extension(p_ip_path); + String required_extension = OpenXRInteractionProfileMetadata::get_singleton()->get_interaction_profile_extension(p_ip_path); // If unsupported is returned we likely have a misspelled interaction profile path in our action map. Always output that as an error. ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported interaction profile " + p_ip_path); @@ -260,9 +260,9 @@ bool OpenXRAPI::interaction_profile_supports_io_path(const String &p_ip_path, co return false; } - const OpenXRInteractionProfileMetaData::IOPath *io_path = OpenXRInteractionProfileMetaData::get_singleton()->get_io_path(p_ip_path, p_io_path); + const OpenXRInteractionProfileMetadata::IOPath *io_path = OpenXRInteractionProfileMetadata::get_singleton()->get_io_path(p_ip_path, p_io_path); - // If the io_path is not part of our meta data we've likely got a misspelled name or a bad action map, report + // If the io_path is not part of our metadata we've likely got a misspelled name or a bad action map, report ERR_FAIL_NULL_V_MSG(io_path, false, "OpenXR: Unsupported io path " + String(p_ip_path) + String(p_io_path)); if (io_path->openxr_extension_name == "") { diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp index 5d636c2b70..d69c803502 100644 --- a/modules/openxr/register_types.cpp +++ b/modules/openxr/register_types.cpp @@ -34,7 +34,7 @@ #include "action_map/openxr_action_map.h" #include "action_map/openxr_action_set.h" #include "action_map/openxr_interaction_profile.h" -#include "action_map/openxr_interaction_profile_meta_data.h" +#include "action_map/openxr_interaction_profile_metadata.h" #include "openxr_interface.h" #include "extensions/openxr_extension_wrapper_extension.h" @@ -69,7 +69,7 @@ #endif static OpenXRAPI *openxr_api = nullptr; -static OpenXRInteractionProfileMetaData *openxr_interaction_profile_meta_data = nullptr; +static OpenXRInteractionProfileMetadata *openxr_interaction_profile_metadata = nullptr; static Ref<OpenXRInterface> openxr_interface; #ifdef TOOLS_ENABLED @@ -77,10 +77,10 @@ static void _editor_init() { if (OpenXRAPI::openxr_is_enabled(false)) { // Only add our OpenXR action map editor if OpenXR is enabled for our project - if (openxr_interaction_profile_meta_data == nullptr) { - // If we didn't initialize our actionmap meta data at startup, we initialize it now. - openxr_interaction_profile_meta_data = memnew(OpenXRInteractionProfileMetaData); - ERR_FAIL_NULL(openxr_interaction_profile_meta_data); + if (openxr_interaction_profile_metadata == nullptr) { + // If we didn't initialize our actionmap metadata at startup, we initialize it now. + openxr_interaction_profile_metadata = memnew(OpenXRInteractionProfileMetadata); + ERR_FAIL_NULL(openxr_interaction_profile_metadata); } OpenXREditorPlugin *openxr_plugin = memnew(OpenXREditorPlugin()); @@ -118,8 +118,8 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { } if (OpenXRAPI::openxr_is_enabled()) { - openxr_interaction_profile_meta_data = memnew(OpenXRInteractionProfileMetaData); - ERR_FAIL_NULL(openxr_interaction_profile_meta_data); + openxr_interaction_profile_metadata = memnew(OpenXRInteractionProfileMetadata); + ERR_FAIL_NULL(openxr_interaction_profile_metadata); openxr_api = memnew(OpenXRAPI); ERR_FAIL_NULL(openxr_api); @@ -150,7 +150,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(OpenXRAction); GDREGISTER_CLASS(OpenXRActionSet); GDREGISTER_CLASS(OpenXRActionMap); - GDREGISTER_CLASS(OpenXRInteractionProfileMetaData); + GDREGISTER_CLASS(OpenXRInteractionProfileMetadata); GDREGISTER_CLASS(OpenXRIPBinding); GDREGISTER_CLASS(OpenXRInteractionProfile); @@ -203,9 +203,9 @@ void uninitialize_openxr_module(ModuleInitializationLevel p_level) { openxr_api = nullptr; } - if (openxr_interaction_profile_meta_data) { - memdelete(openxr_interaction_profile_meta_data); - openxr_interaction_profile_meta_data = nullptr; + if (openxr_interaction_profile_metadata) { + memdelete(openxr_interaction_profile_metadata); + openxr_interaction_profile_metadata = nullptr; } // cleanup our extension wrappers diff --git a/editor/icons/RegEx.svg b/modules/regex/icons/RegEx.svg index 4df26f41c0..4df26f41c0 100644 --- a/editor/icons/RegEx.svg +++ b/modules/regex/icons/RegEx.svg diff --git a/editor/icons/RegExMatch.svg b/modules/regex/icons/RegExMatch.svg index 889cf6cc8a..889cf6cc8a 100644 --- a/editor/icons/RegExMatch.svg +++ b/modules/regex/icons/RegExMatch.svg diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 2a502d081e..804a16423d 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -3780,6 +3780,7 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced * p_shaped->script_iter = nullptr; } p_shaped->break_ops_valid = false; + p_shaped->chars_valid = false; p_shaped->js_ops_valid = false; } } @@ -4833,6 +4834,75 @@ int64_t TextServerAdvanced::_shaped_text_get_ellipsis_glyph_count(const RID &p_s return sd->overrun_trim_data.ellipsis_glyph_buf.size(); } +void TextServerAdvanced::_update_chars(ShapedTextDataAdvanced *p_sd) const { + if (!p_sd->chars_valid) { + p_sd->chars.clear(); + + const UChar *data = p_sd->utf16.get_data(); + UErrorCode err = U_ZERO_ERROR; + int prev = -1; + int i = 0; + + Vector<ShapedTextDataAdvanced::Span> &spans = p_sd->spans; + if (p_sd->parent != RID()) { + ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(p_sd->parent); + ERR_FAIL_COND(!parent_sd->valid); + spans = parent_sd->spans; + } + + while (i < spans.size()) { + if (spans[i].start > p_sd->end) { + break; + } + if (spans[i].end < p_sd->start) { + i++; + continue; + } + + int r_start = MAX(0, spans[i].start - p_sd->start); + String language = spans[i].language; + while (i + 1 < spans.size() && language == spans[i + 1].language) { + i++; + } + int r_end = MIN(spans[i].end - p_sd->start, p_sd->text.length()); + UBreakIterator *bi = ubrk_open(UBRK_CHARACTER, (language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale().ascii().get_data() : language.ascii().get_data(), data + _convert_pos_inv(p_sd, r_start), _convert_pos_inv(p_sd, r_end - r_start), &err); + if (U_SUCCESS(err)) { + while (ubrk_next(bi) != UBRK_DONE) { + int pos = _convert_pos(p_sd, ubrk_current(bi)) + r_start + p_sd->start; + if (prev != pos) { + p_sd->chars.push_back(pos); + } + prev = pos; + } + ubrk_close(bi); + } else { + for (int j = r_start; j < r_end; j++) { + if (prev != j) { + p_sd->chars.push_back(j + 1 + p_sd->start); + } + prev = j; + } + } + i++; + } + p_sd->chars_valid = true; + } +} + +PackedInt32Array TextServerAdvanced::_shaped_text_get_character_breaks(const RID &p_shaped) const { + ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V(!sd, PackedInt32Array()); + + MutexLock lock(sd->mutex); + if (!sd->valid) { + const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped); + } + + _update_chars(sd); + + return sd->chars; +} + bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -5336,7 +5406,17 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star // Try system fallback. RID fdef = p_fonts[0]; if (_font_is_allow_system_fallback(fdef)) { - String text = p_sd->text.substr(p_start, 1); + _update_chars(p_sd); + + int64_t next = p_end; + for (const int32_t &E : p_sd->chars) { + if (E > p_start) { + next = E; + break; + } + } + String text = p_sd->text.substr(p_start, next - p_start); + String font_name = _font_get_name(fdef); BitField<FontStyle> font_style = _font_get_style(fdef); int font_weight = _font_get_weight(fdef); @@ -6600,6 +6680,30 @@ PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_str return ret; } +PackedInt32Array TextServerAdvanced::_string_get_character_breaks(const String &p_string, const String &p_language) const { + const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language; + // Convert to UTF-16. + Char16String utf16 = p_string.utf16(); + + PackedInt32Array ret; + + UErrorCode err = U_ZERO_ERROR; + UBreakIterator *bi = ubrk_open(UBRK_CHARACTER, lang.ascii().get_data(), (const UChar *)utf16.get_data(), utf16.length(), &err); + if (U_SUCCESS(err)) { + while (ubrk_next(bi) != UBRK_DONE) { + int pos = _convert_pos(p_string, utf16, ubrk_current(bi)); + ret.push_back(pos); + } + ubrk_close(bi); + } else { + for (int i = 0; i <= p_string.size(); i++) { + ret.push_back(i); + } + } + + return ret; +} + bool TextServerAdvanced::_is_valid_identifier(const String &p_string) const { #ifndef ICU_STATIC_DATA if (!icu_data_loaded) { diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 44700e045b..f27fa1dee9 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -509,9 +509,11 @@ class TextServerAdvanced : public TextServerExtension { HashMap<int, bool> jstops; HashMap<int, bool> breaks; + PackedInt32Array chars; int break_inserts = 0; bool break_ops_valid = false; bool js_ops_valid = false; + bool chars_valid = false; ~ShapedTextDataAdvanced() { for (int i = 0; i < bidi_iter.size(); i++) { @@ -609,6 +611,7 @@ class TextServerAdvanced : public TextServerExtension { mutable HashMap<SystemFontKey, SystemFontCache, SystemFontKeyHasher> system_fonts; mutable HashMap<String, PackedByteArray> system_font_data; + void _update_chars(ShapedTextDataAdvanced *p_sd) const; void _realign(ShapedTextDataAdvanced *p_sd) const; int64_t _convert_pos(const String &p_utf32, const Char16String &p_utf16, int64_t p_pos) const; int64_t _convert_pos(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const; @@ -920,11 +923,14 @@ public: MODBIND1RC(double, shaped_text_get_underline_position, const RID &); MODBIND1RC(double, shaped_text_get_underline_thickness, const RID &); + MODBIND1RC(PackedInt32Array, shaped_text_get_character_breaks, const RID &); + MODBIND2RC(String, format_number, const String &, const String &); MODBIND2RC(String, parse_number, const String &, const String &); MODBIND1RC(String, percent_sign, const String &); MODBIND3RC(PackedInt32Array, string_get_word_breaks, const String &, const String &, int64_t); + MODBIND2RC(PackedInt32Array, string_get_character_breaks, const String &, const String &); MODBIND2RC(int64_t, is_confusable, const String &, const PackedStringArray &); MODBIND1RC(bool, spoof_check, const String &); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 70da5e2782..4976c70b3b 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -4079,6 +4079,26 @@ double TextServerFallback::_shaped_text_get_underline_thickness(const RID &p_sha return sd->uthk; } +PackedInt32Array TextServerFallback::_shaped_text_get_character_breaks(const RID &p_shaped) const { + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V(!sd, PackedInt32Array()); + + MutexLock lock(sd->mutex); + if (!sd->valid) { + const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped); + } + + PackedInt32Array ret; + int size = sd->end - sd->start; + if (size > 0) { + ret.resize(size); + for (int i = 0; i < size; i++) { + ret.write[i] = i + 1 + sd->start; + } + } + return ret; +} + String TextServerFallback::_string_to_upper(const String &p_string, const String &p_language) const { return p_string.to_upper(); } diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 54311caaf9..457573ecb3 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -788,6 +788,8 @@ public: MODBIND1RC(double, shaped_text_get_underline_position, const RID &); MODBIND1RC(double, shaped_text_get_underline_thickness, const RID &); + MODBIND1RC(PackedInt32Array, shaped_text_get_character_breaks, const RID &); + MODBIND3RC(PackedInt32Array, string_get_word_breaks, const String &, const String &, int64_t); MODBIND2RC(String, string_to_upper, const String &, const String &); diff --git a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml index c2dcb832e0..dd6c181eae 100644 --- a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml +++ b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml @@ -9,7 +9,7 @@ Ogg Vorbis requires more CPU to decode than [ResourceImporterWAV]. If you need to play a lot of simultaneous sounds, it's recommended to use WAV for those sounds instead, especially if targeting low-end devices. </description> <tutorials> - <link title="Importing audio samples">https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_audio_samples.html</link> + <link title="Importing audio samples">$DOCS_URL/tutorials/assets_pipeline/importing_audio_samples.html</link> </tutorials> <methods> <method name="load_from_buffer" qualifiers="static"> diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml index d270980d72..0f1da54376 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -8,6 +8,7 @@ <tutorials> <link title="Exporting for Android">$DOCS_URL/tutorials/export/exporting_for_android.html</link> <link title="Custom builds for Android">$DOCS_URL/tutorials/export/android_custom_build.html</link> + <link title="Android plugins documentation index">$DOCS_URL/tutorials/platform/index.html</link> </tutorials> <members> <member name="apk_expansion/SALT" type="String" setter="" getter=""> diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 32b46271fd..682603e46b 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -53,6 +53,9 @@ void register_android_exporter() { EDITOR_DEF("export/android/shutdown_adb_on_exit", true); EDITOR_DEF("export/android/one_click_deploy_clear_previous_install", false); + + EDITOR_DEF("export/android/use_wifi_for_remote_debug", false); + EDITOR_DEF("export/android/wifi_remote_debug_host", "localhost"); #endif Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid)); diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 21de46f4fc..15ded5c61e 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1954,8 +1954,9 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, return ERR_SKIP; } + const bool use_wifi_for_remote_debug = EDITOR_GET("export/android/use_wifi_for_remote_debug"); const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); - const bool use_reverse = devices[p_device].api_level >= 21; + const bool use_reverse = devices[p_device].api_level >= 21 && !use_wifi_for_remote_debug; if (use_reverse) { p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; @@ -2068,7 +2069,10 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, print_line("Reverse result2: " + itos(rv)); } } else { - static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---"; + static const char *const api_version_msg = "--- Device API < 21; debugging over Wi-Fi ---"; + static const char *const manual_override_msg = "--- Wi-Fi remote debug enabled in project settings; debugging over Wi-Fi ---"; + + const char *const msg = use_wifi_for_remote_debug ? manual_override_msg : api_version_msg; EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); print_line(String(msg).to_upper()); } diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 563c057266..ecae6d721e 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -7,6 +7,7 @@ </description> <tutorials> <link title="Exporting for iOS">$DOCS_URL/tutorials/export/exporting_for_ios.html</link> + <link title="iOS plugins documentation index">$DOCS_URL/tutorials/platform/ios/index.html</link> </tutorials> <members> <member name="application/app_store_team_id" type="String" setter="" getter=""> diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml index c24ff4cb73..7ba1379fd0 100644 --- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml +++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml @@ -155,7 +155,7 @@ If enabled, a wrapper that can be used to run the application with console output is created alongside the exported application. </member> <member name="display/high_res" type="bool" setter="" getter=""> - If [code]true[/code], the application is rendered at native display resolution, otherwise it is always rendered at loHPI resolution and upscaled by OS when required. + If [code]true[/code], the application is rendered at native display resolution, otherwise it is always rendered at loDPI resolution and upscaled by OS when required. </member> <member name="export/distribution_type" type="int" setter="" getter=""> Application distribution target. diff --git a/platform/web/doc_classes/EditorExportPlatformWeb.xml b/platform/web/doc_classes/EditorExportPlatformWeb.xml index 607fdd0533..c4c4fd870b 100644 --- a/platform/web/doc_classes/EditorExportPlatformWeb.xml +++ b/platform/web/doc_classes/EditorExportPlatformWeb.xml @@ -7,6 +7,7 @@ </description> <tutorials> <link title="Exporting for the Web">$DOCS_URL/tutorials/export/exporting_for_web.html</link> + <link title="Web documentation index">$DOCS_URL/tutorials/platform/web/index.html</link> </tutorials> <members> <member name="custom_template/debug" type="String" setter="" getter=""> diff --git a/platform/web/js/libs/library_godot_javascript_singleton.js b/platform/web/js/libs/library_godot_javascript_singleton.js index dafc01a1fd..1f3633461b 100644 --- a/platform/web/js/libs/library_godot_javascript_singleton.js +++ b/platform/web/js/libs/library_godot_javascript_singleton.js @@ -109,7 +109,7 @@ const GodotJSWrapper = { return 2; // INT } GodotRuntime.setHeapValue(p_exchange, p_val, 'double'); - return 3; // REAL + return 3; // FLOAT } else if (type === 'string') { const c_str = GodotRuntime.allocString(p_val); GodotRuntime.setHeapValue(p_exchange, c_str, '*'); @@ -313,7 +313,7 @@ const GodotEval = { case 'number': GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'double'); - return 3; // REAL + return 3; // FLOAT case 'string': GodotRuntime.setHeapValue(p_union_ptr, GodotRuntime.allocString(eval_ret), '*'); @@ -333,7 +333,7 @@ const GodotEval = { const func = GodotRuntime.get_func(p_callback); const bytes_ptr = func(p_byte_arr, p_byte_arr_write, eval_ret.length); HEAPU8.set(eval_ret, bytes_ptr); - return 20; // POOL_BYTE_ARRAY + return 29; // PACKED_BYTE_ARRAY } break; diff --git a/platform/windows/SCsub b/platform/windows/SCsub index efbb47d965..d7dd224199 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -44,6 +44,7 @@ if env["windows_subsystem"] == "gui": env_wrap.Append(LIBS=["version"]) prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"]) + env_wrap.Depends(prog_wrap, prog) # Microsoft Visual Studio Project Generation if env["vsproj"]: diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 7c94e38e14..b661222dfe 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1356,18 +1356,13 @@ Error OS_Windows::shell_open(String p_uri) { } Error OS_Windows::shell_show_in_file_manager(String p_path, bool p_open_folder) { - p_path = p_path.trim_suffix("file://"); - bool open_folder = false; if (DirAccess::dir_exists_absolute(p_path) && p_open_folder) { open_folder = true; } - if (p_path.begins_with("\"")) { - p_path = String("\"") + p_path; - } - if (p_path.ends_with("\"")) { - p_path = p_path + String("\""); + if (!p_path.is_quoted()) { + p_path = p_path.quote(); } p_path = p_path.replace("/", "\\"); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 8c5782dc41..70c72dab07 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -30,7 +30,10 @@ #include "gpu_particles_2d.h" +#include "scene/2d/cpu_particles_2d.h" #include "scene/resources/atlas_texture.h" +#include "scene/resources/curve_texture.h" +#include "scene/resources/gradient_texture.h" #include "scene/resources/particle_process_material.h" #include "scene/scene_string_names.h" @@ -435,6 +438,97 @@ void GPUParticles2D::restart() { } } +void GPUParticles2D::convert_from_particles(Node *p_particles) { + CPUParticles2D *cpu_particles = Object::cast_to<CPUParticles2D>(p_particles); + ERR_FAIL_NULL_MSG(cpu_particles, "Only CPUParticles2D nodes can be converted to GPUParticles2D."); + + set_emitting(cpu_particles->is_emitting()); + set_amount(cpu_particles->get_amount()); + set_lifetime(cpu_particles->get_lifetime()); + set_one_shot(cpu_particles->get_one_shot()); + set_pre_process_time(cpu_particles->get_pre_process_time()); + set_explosiveness_ratio(cpu_particles->get_explosiveness_ratio()); + set_randomness_ratio(cpu_particles->get_randomness_ratio()); + set_use_local_coordinates(cpu_particles->get_use_local_coordinates()); + set_fixed_fps(cpu_particles->get_fixed_fps()); + set_fractional_delta(cpu_particles->get_fractional_delta()); + set_speed_scale(cpu_particles->get_speed_scale()); + set_draw_order(DrawOrder(cpu_particles->get_draw_order())); + set_texture(cpu_particles->get_texture()); + + Ref<Material> mat = cpu_particles->get_material(); + if (mat.is_valid()) { + set_material(mat); + } + + Ref<ParticleProcessMaterial> proc_mat = memnew(ParticleProcessMaterial); + set_process_material(proc_mat); + Vector2 dir = cpu_particles->get_direction(); + proc_mat->set_direction(Vector3(dir.x, dir.y, 0)); + proc_mat->set_spread(cpu_particles->get_spread()); + proc_mat->set_color(cpu_particles->get_color()); + + Ref<Gradient> color_grad = cpu_particles->get_color_ramp(); + if (color_grad.is_valid()) { + Ref<GradientTexture1D> tex = memnew(GradientTexture1D); + tex->set_gradient(color_grad); + proc_mat->set_color_ramp(tex); + } + + Ref<Gradient> color_init_grad = cpu_particles->get_color_initial_ramp(); + if (color_init_grad.is_valid()) { + Ref<GradientTexture1D> tex = memnew(GradientTexture1D); + tex->set_gradient(color_init_grad); + proc_mat->set_color_initial_ramp(tex); + } + + proc_mat->set_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, cpu_particles->get_particle_flag(CPUParticles2D::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); + + proc_mat->set_emission_shape(ParticleProcessMaterial::EmissionShape(cpu_particles->get_emission_shape())); + proc_mat->set_emission_sphere_radius(cpu_particles->get_emission_sphere_radius()); + + Vector2 rect_extents = cpu_particles->get_emission_rect_extents(); + proc_mat->set_emission_box_extents(Vector3(rect_extents.x, rect_extents.y, 0)); + + if (cpu_particles->get_split_scale()) { + Ref<CurveXYZTexture> scale3D = memnew(CurveXYZTexture); + scale3D->set_curve_x(cpu_particles->get_scale_curve_x()); + scale3D->set_curve_y(cpu_particles->get_scale_curve_y()); + proc_mat->set_param_texture(ParticleProcessMaterial::PARAM_SCALE, scale3D); + } + + Vector2 gravity = cpu_particles->get_gravity(); + proc_mat->set_gravity(Vector3(gravity.x, gravity.y, 0)); + proc_mat->set_lifetime_randomness(cpu_particles->get_lifetime_randomness()); + +#define CONVERT_PARAM(m_param) \ + proc_mat->set_param_min(ParticleProcessMaterial::m_param, cpu_particles->get_param_min(CPUParticles2D::m_param)); \ + { \ + Ref<Curve> curve = cpu_particles->get_param_curve(CPUParticles2D::m_param); \ + if (curve.is_valid()) { \ + Ref<CurveTexture> tex = memnew(CurveTexture); \ + tex->set_curve(curve); \ + proc_mat->set_param_texture(ParticleProcessMaterial::m_param, tex); \ + } \ + } \ + proc_mat->set_param_max(ParticleProcessMaterial::m_param, cpu_particles->get_param_max(CPUParticles2D::m_param)); + + CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); + CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); + CONVERT_PARAM(PARAM_ORBIT_VELOCITY); + CONVERT_PARAM(PARAM_LINEAR_ACCEL); + CONVERT_PARAM(PARAM_RADIAL_ACCEL); + CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL); + CONVERT_PARAM(PARAM_DAMPING); + CONVERT_PARAM(PARAM_ANGLE); + CONVERT_PARAM(PARAM_SCALE); + CONVERT_PARAM(PARAM_HUE_VARIATION); + CONVERT_PARAM(PARAM_ANIM_SPEED); + CONVERT_PARAM(PARAM_ANIM_OFFSET); + +#undef CONVERT_PARAM +} + void GPUParticles2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { @@ -680,6 +774,8 @@ void GPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_trail_section_subdivisions", "subdivisions"), &GPUParticles2D::set_trail_section_subdivisions); ClassDB::bind_method(D_METHOD("get_trail_section_subdivisions"), &GPUParticles2D::get_trail_section_subdivisions); + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &GPUParticles2D::convert_from_particles); + ADD_SIGNAL(MethodInfo("finished")); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index 3131698e5c..97690b07fa 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -169,6 +169,8 @@ public: void restart(); Rect2 capture_rect() const; + void convert_from_particles(Node *p_particles); + GPUParticles2D(); ~GPUParticles2D(); }; diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 4b7fad5edf..fb3b199638 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -138,10 +138,6 @@ void Node2D::_update_transform() { RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform); - if (!is_inside_tree()) { - return; - } - _notify_transform(); } @@ -378,10 +374,6 @@ void Node2D::set_transform(const Transform2D &p_transform) { RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform); - if (!is_inside_tree()) { - return; - } - _notify_transform(); } diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 3a23cbcff1..a7667267a6 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -30,6 +30,9 @@ #include "gpu_particles_3d.h" +#include "scene/3d/cpu_particles_3d.h" +#include "scene/resources/curve_texture.h" +#include "scene/resources/gradient_texture.h" #include "scene/resources/particle_process_material.h" #include "scene/scene_string_names.h" @@ -546,10 +549,98 @@ void GPUParticles3D::set_transform_align(TransformAlign p_align) { transform_align = p_align; RS::get_singleton()->particles_set_transform_align(particles, RS::ParticlesTransformAlign(transform_align)); } + GPUParticles3D::TransformAlign GPUParticles3D::get_transform_align() const { return transform_align; } +void GPUParticles3D::convert_from_particles(Node *p_particles) { + CPUParticles3D *cpu_particles = Object::cast_to<CPUParticles3D>(p_particles); + ERR_FAIL_NULL_MSG(cpu_particles, "Only CPUParticles3D nodes can be converted to GPUParticles3D."); + + set_emitting(cpu_particles->is_emitting()); + set_amount(cpu_particles->get_amount()); + set_lifetime(cpu_particles->get_lifetime()); + set_one_shot(cpu_particles->get_one_shot()); + set_pre_process_time(cpu_particles->get_pre_process_time()); + set_explosiveness_ratio(cpu_particles->get_explosiveness_ratio()); + set_randomness_ratio(cpu_particles->get_randomness_ratio()); + set_use_local_coordinates(cpu_particles->get_use_local_coordinates()); + set_fixed_fps(cpu_particles->get_fixed_fps()); + set_fractional_delta(cpu_particles->get_fractional_delta()); + set_speed_scale(cpu_particles->get_speed_scale()); + set_draw_order(DrawOrder(cpu_particles->get_draw_order())); + set_draw_pass_mesh(0, cpu_particles->get_mesh()); + + Ref<ParticleProcessMaterial> proc_mat = memnew(ParticleProcessMaterial); + set_process_material(proc_mat); + + proc_mat->set_direction(cpu_particles->get_direction()); + proc_mat->set_spread(cpu_particles->get_spread()); + proc_mat->set_flatness(cpu_particles->get_flatness()); + proc_mat->set_color(cpu_particles->get_color()); + + Ref<Gradient> grad = cpu_particles->get_color_ramp(); + if (grad.is_valid()) { + Ref<GradientTexture1D> tex = memnew(GradientTexture1D); + tex->set_gradient(grad); + proc_mat->set_color_ramp(tex); + } + + Ref<Gradient> grad_init = cpu_particles->get_color_initial_ramp(); + if (grad_init.is_valid()) { + Ref<GradientTexture1D> tex = memnew(GradientTexture1D); + tex->set_gradient(grad_init); + proc_mat->set_color_initial_ramp(tex); + } + + proc_mat->set_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, cpu_particles->get_particle_flag(CPUParticles3D::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); + proc_mat->set_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_ROTATE_Y, cpu_particles->get_particle_flag(CPUParticles3D::PARTICLE_FLAG_ROTATE_Y)); + proc_mat->set_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_DISABLE_Z, cpu_particles->get_particle_flag(CPUParticles3D::PARTICLE_FLAG_DISABLE_Z)); + + proc_mat->set_emission_shape(ParticleProcessMaterial::EmissionShape(cpu_particles->get_emission_shape())); + proc_mat->set_emission_sphere_radius(cpu_particles->get_emission_sphere_radius()); + proc_mat->set_emission_box_extents(cpu_particles->get_emission_box_extents()); + + if (cpu_particles->get_split_scale()) { + Ref<CurveXYZTexture> scale3D = memnew(CurveXYZTexture); + scale3D->set_curve_x(cpu_particles->get_scale_curve_x()); + scale3D->set_curve_y(cpu_particles->get_scale_curve_y()); + scale3D->set_curve_z(cpu_particles->get_scale_curve_z()); + proc_mat->set_param_texture(ParticleProcessMaterial::PARAM_SCALE, scale3D); + } + + proc_mat->set_gravity(cpu_particles->get_gravity()); + proc_mat->set_lifetime_randomness(cpu_particles->get_lifetime_randomness()); + +#define CONVERT_PARAM(m_param) \ + proc_mat->set_param_min(ParticleProcessMaterial::m_param, cpu_particles->get_param_min(CPUParticles3D::m_param)); \ + { \ + Ref<Curve> curve = cpu_particles->get_param_curve(CPUParticles3D::m_param); \ + if (curve.is_valid()) { \ + Ref<CurveTexture> tex = memnew(CurveTexture); \ + tex->set_curve(curve); \ + proc_mat->set_param_texture(ParticleProcessMaterial::m_param, tex); \ + } \ + } \ + proc_mat->set_param_max(ParticleProcessMaterial::m_param, cpu_particles->get_param_max(CPUParticles3D::m_param)); + + CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); + CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); + CONVERT_PARAM(PARAM_ORBIT_VELOCITY); + CONVERT_PARAM(PARAM_LINEAR_ACCEL); + CONVERT_PARAM(PARAM_RADIAL_ACCEL); + CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL); + CONVERT_PARAM(PARAM_DAMPING); + CONVERT_PARAM(PARAM_ANGLE); + CONVERT_PARAM(PARAM_SCALE); + CONVERT_PARAM(PARAM_HUE_VARIATION); + CONVERT_PARAM(PARAM_ANIM_SPEED); + CONVERT_PARAM(PARAM_ANIM_OFFSET); + +#undef CONVERT_PARAM +} + void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &GPUParticles3D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &GPUParticles3D::set_amount); @@ -613,6 +704,8 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align); ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align); + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &GPUParticles3D::convert_from_particles); + ADD_SIGNAL(MethodInfo("finished")); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index dba6a8f2ab..6e9083bda2 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -178,6 +178,8 @@ public: void emit_particle(const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags); AABB capture_aabb() const; + void convert_from_particles(Node *p_particles); + GPUParticles3D(); ~GPUParticles3D(); }; diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 7b535f6169..a76488e479 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -458,7 +458,7 @@ void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) { #ifdef TOOLS_ENABLED // PolygonOccluder3D is edited via an editor plugin, this ensures the plugin is shown/hidden when necessary if (Engine::get_singleton()->is_editor_hint()) { - EditorNode::get_singleton()->call_deferred(SNAME("edit_current")); + callable_mp(EditorNode::get_singleton(), &EditorNode::edit_current).call_deferred(); } #endif } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 0ca04faf9b..63692dd064 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1734,6 +1734,10 @@ void Control::_size_changed() { if (pos_changed && !size_changed) { _update_canvas_item_transform(); //move because it won't be updated } + } else { + if (pos_changed) { + _notify_transform(); + } } } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 42ee6cb0bc..956e93466c 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -79,7 +79,7 @@ void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) { if (caret_mid_grapheme_enabled) { set_caret_column(get_caret_column() - 1); } else { - set_caret_column(TS->shaped_text_prev_grapheme_pos(text_rid, get_caret_column())); + set_caret_column(TS->shaped_text_prev_character_pos(text_rid, get_caret_column())); } } @@ -112,7 +112,7 @@ void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) { if (caret_mid_grapheme_enabled) { set_caret_column(get_caret_column() + 1); } else { - set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, get_caret_column())); + set_caret_column(TS->shaped_text_next_character_pos(text_rid, get_caret_column())); } } @@ -211,7 +211,7 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { delete_char(); } else { int cc = caret_column; - set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, caret_column)); + set_caret_column(TS->shaped_text_next_character_pos(text_rid, caret_column)); delete_text(cc, caret_column); } } @@ -1326,6 +1326,9 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) { } int ofs = ceil(TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset)); + if (!caret_mid_grapheme_enabled) { + ofs = TS->shaped_text_closest_character_pos(text_rid, ofs); + } set_caret_column(ofs); } diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index dba81b7b50..734813f8a0 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -113,7 +113,7 @@ private: PopupMenu *menu_dir = nullptr; PopupMenu *menu_ctl = nullptr; - bool caret_mid_grapheme_enabled = true; + bool caret_mid_grapheme_enabled = false; int caret_column = 0; float scroll_offset = 0.0; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 1e7e376fc8..9ebcf18ff6 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1585,6 +1585,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } } else { char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); + char_pos = TS->shaped_text_closest_character_pos(rid, char_pos); } } line_clicked = true; @@ -2563,7 +2564,10 @@ String RichTextLabel::_find_language(Item *p_item) { Item *item = p_item; while (item) { - if (item->type == ITEM_PARAGRAPH) { + if (item->type == ITEM_LANGUAGE) { + ItemLanguage *p = static_cast<ItemLanguage *>(item); + return p->language; + } else if (item->type == ITEM_PARAGRAPH) { ItemParagraph *p = static_cast<ItemParagraph *>(item); return p->language; } @@ -3091,15 +3095,17 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub // If a newline was erased, all lines AFTER the newline need to be decremented. if (p_item->type == ITEM_NEWLINE) { current_frame->lines.remove_at(p_line); - for (int i = 0; i < current->subitems.size(); i++) { - if (current->subitems[i]->line > p_subitem_line) { - current->subitems[i]->line--; + if (p_line < (int)current_frame->lines.size() && current_frame->lines[p_line].from) { + for (List<Item *>::Element *E = current_frame->lines[p_line].from->E; E; E = E->next()) { + if (E->get()->line > p_subitem_line) { + E->get()->line--; + } } } } } else { // First, remove all child items for the provided item. - for (int i = 0; i < size; i++) { + while (p_item->subitems.size()) { _remove_item(p_item->subitems.front()->get(), p_line, p_subitem_line); } // Then remove the provided item itself. @@ -3195,34 +3201,40 @@ bool RichTextLabel::remove_paragraph(const int p_paragraph) { } // Remove all subitems with the same line as that provided. - Vector<int> subitem_indices_to_remove; - for (int i = 0; i < current->subitems.size(); i++) { - if (current->subitems[i]->line == p_paragraph) { - subitem_indices_to_remove.push_back(i); + Vector<List<Item *>::Element *> subitem_to_remove; + if (current_frame->lines[p_paragraph].from) { + for (List<Item *>::Element *E = current_frame->lines[p_paragraph].from->E; E; E = E->next()) { + if (E->get()->line == p_paragraph) { + subitem_to_remove.push_back(E); + } else { + break; + } } } bool had_newline = false; // Reverse for loop to remove items from the end first. - for (int i = subitem_indices_to_remove.size() - 1; i >= 0; i--) { - int subitem_idx = subitem_indices_to_remove[i]; - had_newline = had_newline || current->subitems[subitem_idx]->type == ITEM_NEWLINE; - _remove_item(current->subitems[subitem_idx], current->subitems[subitem_idx]->line, p_paragraph); + for (int i = subitem_to_remove.size() - 1; i >= 0; i--) { + List<Item *>::Element *subitem = subitem_to_remove[i]; + had_newline = had_newline || subitem->get()->type == ITEM_NEWLINE; + _remove_item(subitem->get(), subitem->get()->line, p_paragraph); } if (!had_newline) { current_frame->lines.remove_at(p_paragraph); - if (current_frame->lines.size() == 0) { - current_frame->lines.resize(1); - } + } + + if (current_frame->lines.is_empty()) { + current_frame->lines.resize(1); } if (p_paragraph == 0 && current->subitems.size() > 0) { main->lines[0].from = main; } - int to_line = main->first_invalid_line.load(); - main->first_invalid_line.store(MIN(to_line, p_paragraph)); + main->first_invalid_line.store(MIN(main->first_invalid_line.load(), p_paragraph)); + main->first_resized_line.store(MIN(main->first_resized_line.load(), p_paragraph)); + main->first_invalid_font_line.store(MIN(main->first_invalid_font_line.load(), p_paragraph)); queue_redraw(); return true; @@ -3440,6 +3452,17 @@ void RichTextLabel::push_meta(const Variant &p_meta) { _add_item(item, true); } +void RichTextLabel::push_language(const String &p_language) { + _stop_thread(); + MutexLock data_lock(data_mutex); + + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemLanguage *item = memnew(ItemLanguage); + + item->language = p_language; + _add_item(item, true); +} + void RichTextLabel::push_hint(const String &p_string) { _stop_thread(); MutexLock data_lock(data_mutex); @@ -4221,6 +4244,11 @@ void RichTextLabel::append_text(const String &p_bbcode) { push_indent(indent_level); pos = brk_end + 1; tag_stack.push_front(tag); + } else if (tag.begins_with("lang=")) { + String lang = tag.substr(5, tag.length()).unquote(); + push_language(lang); + pos = brk_end + 1; + tag_stack.push_front("lang"); } else if (tag == "p") { push_paragraph(HORIZONTAL_ALIGNMENT_LEFT); pos = brk_end + 1; @@ -5282,6 +5310,8 @@ void RichTextLabel::selection_copy() { } void RichTextLabel::select_all() { + _validate_line_caches(); + if (!selection.enabled) { return; } @@ -5294,13 +5324,12 @@ void RichTextLabel::select_all() { if (it->type != ITEM_FRAME) { if (!from_item) { from_item = it; - } else { - to_item = it; } + to_item = it; } it = _get_next_item(it, true); } - if (!from_item || !to_item) { + if (!from_item) { return; } @@ -5606,6 +5635,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize", "bullet"), &RichTextLabel::push_list, DEFVAL(String::utf8("•"))); ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); ClassDB::bind_method(D_METHOD("push_hint", "description"), &RichTextLabel::push_hint); + ClassDB::bind_method(D_METHOD("push_language", "language"), &RichTextLabel::push_language); ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline); ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough); ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align", "align_to_row"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGNMENT_TOP), DEFVAL(-1)); @@ -5846,13 +5876,21 @@ int RichTextLabel::get_character_line(int p_char) { int char_offset = main->lines[i].char_offset; int char_count = main->lines[i].char_count; if (char_offset <= p_char && p_char < char_offset + char_count) { - for (int j = 0; j < main->lines[i].text_buf->get_line_count(); j++) { + int lc = main->lines[i].text_buf->get_line_count(); + for (int j = 0; j < lc; j++) { Vector2i range = main->lines[i].text_buf->get_line_range(j); - if (char_offset + range.x <= p_char && p_char <= char_offset + range.y) { - return line_count; + if (char_offset + range.x <= p_char && p_char < char_offset + range.y) { + break; + } + if (char_offset + range.x > p_char && line_count > 0) { + line_count--; // Character is not rendered and is between the lines (e.g., edge space). + break; + } + if (j != lc - 1) { + line_count++; } - line_count++; } + return line_count; } else { line_count += main->lines[i].text_buf->get_line_count(); } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 0d0917a9d8..a3230cf13c 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -77,7 +77,8 @@ public: ITEM_HINT, ITEM_DROPCAP, ITEM_CUSTOMFX, - ITEM_CONTEXT + ITEM_CONTEXT, + ITEM_LANGUAGE, }; enum MenuItems { @@ -237,6 +238,11 @@ private: ItemHint() { type = ITEM_HINT; } }; + struct ItemLanguage : public Item { + String language; + ItemLanguage() { type = ITEM_LANGUAGE; } + }; + struct ItemParagraph : public Item { HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; String language; @@ -615,6 +621,7 @@ public: void push_outline_color(const Color &p_color); void push_underline(); void push_strikethrough(); + void push_language(const String &p_language); void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", TextServer::StructuredTextParser p_st_parser = TextServer::STRUCTURED_TEXT_DEFAULT, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE, const PackedFloat32Array &p_tab_stops = PackedFloat32Array()); void push_indent(int p_level); void push_list(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet = String::utf8("•")); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 3b2013f7ec..cffd9604f6 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2374,7 +2374,7 @@ void TextEdit::_move_caret_left(bool p_select, bool p_move_by_word) { if (caret_mid_grapheme_enabled) { set_caret_column(get_caret_column(i) - 1, i == 0, i); } else { - set_caret_column(TS->shaped_text_prev_grapheme_pos(text.get_line_data(get_caret_line(i))->get_rid(), get_caret_column(i)), i == 0, i); + set_caret_column(TS->shaped_text_prev_character_pos(text.get_line_data(get_caret_line(i))->get_rid(), get_caret_column(i)), i == 0, i); } } } @@ -2433,7 +2433,7 @@ void TextEdit::_move_caret_right(bool p_select, bool p_move_by_word) { if (caret_mid_grapheme_enabled) { set_caret_column(get_caret_column(i) + 1, i == 0, i); } else { - set_caret_column(TS->shaped_text_next_grapheme_pos(text.get_line_data(get_caret_line(i))->get_rid(), get_caret_column(i)), i == 0, i); + set_caret_column(TS->shaped_text_next_character_pos(text.get_line_data(get_caret_line(i))->get_rid(), get_caret_column(i)), i == 0, i); } } } @@ -2815,7 +2815,7 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) { if (caret_mid_grapheme_enabled) { next_column = get_caret_column(caret_idx) < curline_len ? (get_caret_column(caret_idx) + 1) : 0; } else { - next_column = get_caret_column(caret_idx) < curline_len ? TS->shaped_text_next_grapheme_pos(text.get_line_data(get_caret_line(caret_idx))->get_rid(), (get_caret_column(caret_idx))) : 0; + next_column = get_caret_column(caret_idx) < curline_len ? TS->shaped_text_next_character_pos(text.get_line_data(get_caret_line(caret_idx))->get_rid(), (get_caret_column(caret_idx))) : 0; } // Remove overlapping carets. @@ -4331,6 +4331,9 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_ colx = TS->shaped_text_get_size(text_rid).x - colx; } col = TS->shaped_text_hit_test_position(text_rid, colx); + if (!caret_mid_grapheme_enabled) { + col = TS->shaped_text_closest_character_pos(text_rid, col); + } return Point2i(col, row); } @@ -6997,6 +7000,11 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc col = p_search.findn(p_key, p_from_column); } + // If not found, just break early to improve performance. + if (col == -1) { + break; + } + // Whole words only. if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) { p_from_column = col; @@ -7023,7 +7031,11 @@ int TextEdit::_get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) con if (is_layout_rtl()) { p_px = TS->shaped_text_get_size(text_rid).x - p_px; } - return TS->shaped_text_hit_test_position(text_rid, p_px); + int ofs = TS->shaped_text_hit_test_position(text_rid, p_px); + if (!caret_mid_grapheme_enabled) { + ofs = TS->shaped_text_closest_character_pos(text_rid, ofs); + } + return ofs; } /* Caret */ diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 83f6d58bea..b52fde9361 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -423,7 +423,7 @@ private: bool move_caret_on_right_click = true; - bool caret_mid_grapheme_enabled = true; + bool caret_mid_grapheme_enabled = false; bool multi_carets_enabled = true; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 3c1be2d5fe..6daa330ee0 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -352,6 +352,24 @@ TextServer::AutowrapMode TreeItem::get_autowrap_mode(int p_column) const { return cells[p_column].autowrap_mode; } +void TreeItem::set_text_overrun_behavior(int p_column, TextServer::OverrunBehavior p_behavior) { + ERR_FAIL_INDEX(p_column, cells.size()); + + if (cells[p_column].text_buf->get_text_overrun_behavior() == p_behavior) { + return; + } + + cells.write[p_column].text_buf->set_text_overrun_behavior(p_behavior); + cells.write[p_column].dirty = true; + _changed_notify(p_column); + cells.write[p_column].cached_minimum_size_dirty = true; +} + +TextServer::OverrunBehavior TreeItem::get_text_overrun_behavior(int p_column) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::OVERRUN_TRIM_ELLIPSIS); + return cells[p_column].text_buf->get_text_overrun_behavior(); +} + void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser) { ERR_FAIL_INDEX(p_column, cells.size()); @@ -1512,6 +1530,9 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_autowrap_mode", "column", "autowrap_mode"), &TreeItem::set_autowrap_mode); ClassDB::bind_method(D_METHOD("get_autowrap_mode", "column"), &TreeItem::get_autowrap_mode); + ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "column", "overrun_behavior"), &TreeItem::set_text_overrun_behavior); + ClassDB::bind_method(D_METHOD("get_text_overrun_behavior", "column"), &TreeItem::get_text_overrun_behavior); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "column", "parser"), &TreeItem::set_structured_text_bidi_override); ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override", "column"), &TreeItem::get_structured_text_bidi_override); @@ -2096,7 +2117,12 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 buttons_width += button_size.width + theme_cache.button_margin; } - p_item->cells.write[i].text_buf->set_width(item_width); + int text_width = item_width - theme_cache.inner_item_margin_left - theme_cache.inner_item_margin_right; + if (p_item->cells[i].icon.is_valid()) { + text_width -= p_item->cells[i].get_icon_size().x + theme_cache.h_separation; + } + + p_item->cells.write[i].text_buf->set_width(text_width); r_self_height = compute_item_height(p_item); label_h = r_self_height + theme_cache.v_separation; @@ -2224,7 +2250,6 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 Point2i text_pos = item_rect.position; text_pos.y += Math::floor(p_draw_ofs.y) - _get_title_button_height(); - int text_width = p_item->cells[i].text_buf->get_size().x; switch (p_item->cells[i].mode) { case TreeItem::CELL_MODE_STRING: { diff --git a/scene/gui/tree.h b/scene/gui/tree.h index cb00889cb9..4afe94a3a0 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -117,6 +117,7 @@ private: Cell() { text_buf.instantiate(); + text_buf->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); } Size2 get_icon_size() const; @@ -231,6 +232,9 @@ public: void set_autowrap_mode(int p_column, TextServer::AutowrapMode p_mode); TextServer::AutowrapMode get_autowrap_mode(int p_column) const; + void set_text_overrun_behavior(int p_column, TextServer::OverrunBehavior p_behavior); + TextServer::OverrunBehavior get_text_overrun_behavior(int p_column) const; + void set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser); TextServer::StructuredTextParser get_structured_text_bidi_override(int p_column) const; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 2947d73552..855cad51e9 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -320,7 +320,6 @@ void CanvasItem::_notification(int p_what) { } } - _set_global_invalid(true); _enter_canvas(); RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, is_visible_in_tree()); // The visibility of the parent may change. @@ -367,7 +366,11 @@ void CanvasItem::_notification(int p_what) { case NOTIFICATION_WORLD_2D_CHANGED: { _exit_canvas(); _enter_canvas(); - } + } break; + case NOTIFICATION_PARENTED: { + // The node is not inside the tree during this notification. + _notify_transform(); + } break; } } diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 7f6bbb17c3..51591e1ac6 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -152,11 +152,8 @@ private: protected: _FORCE_INLINE_ void _notify_transform() { - if (!is_inside_tree()) { - return; - } _notify_transform(this); - if (!block_transform_notify && notify_local_transform) { + if (is_inside_tree() && !block_transform_notify && notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); } } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 7b2d67d776..e862a76b92 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -368,6 +368,7 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { return; } + // The index needs to be update before every usage in case an event callback changed the window list. int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); @@ -379,6 +380,8 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; } // Can only move to foreground, but no focus granted. + index = _sub_window_find(p_window); + ERR_FAIL_COND(index == -1); SubWindow sw = gui.sub_windows[index]; gui.sub_windows.remove_at(index); gui.sub_windows.push_back(sw); @@ -406,6 +409,8 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); { // Move to foreground. + index = _sub_window_find(p_window); + ERR_FAIL_COND(index == -1); SubWindow sw = gui.sub_windows[index]; gui.sub_windows.remove_at(index); gui.sub_windows.push_back(sw); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 1af279d94c..cffbb794c5 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -342,6 +342,25 @@ Point2i Window::get_position() const { return position; } +void Window::move_to_center() { + ERR_MAIN_THREAD_GUARD; + ERR_FAIL_COND(!is_inside_tree()); + + Rect2 parent_rect; + + if (is_embedded()) { + parent_rect = get_embedder()->get_visible_rect(); + } else { + int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(get_window_id()); + parent_rect.position = DisplayServer::get_singleton()->screen_get_position(parent_screen); + parent_rect.size = DisplayServer::get_singleton()->screen_get_size(parent_screen); + } + + if (parent_rect != Rect2()) { + set_position(parent_rect.position + (parent_rect.size - get_size()) / 2); + } +} + void Window::set_size(const Size2i &p_size) { ERR_MAIN_THREAD_GUARD; @@ -2602,6 +2621,7 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_position", "position"), &Window::set_position); ClassDB::bind_method(D_METHOD("get_position"), &Window::get_position); + ClassDB::bind_method(D_METHOD("move_to_center"), &Window::move_to_center); ClassDB::bind_method(D_METHOD("set_size", "size"), &Window::set_size); ClassDB::bind_method(D_METHOD("get_size"), &Window::get_size); diff --git a/scene/main/window.h b/scene/main/window.h index c387ffa92a..d781f228d2 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -246,6 +246,7 @@ public: void set_position(const Point2i &p_position); Point2i get_position() const; + void move_to_center(); void set_size(const Size2i &p_size); Size2i get_size() const; diff --git a/scene/resources/atlas_texture.cpp b/scene/resources/atlas_texture.cpp index 24bf25f2db..6aed68849b 100644 --- a/scene/resources/atlas_texture.cpp +++ b/scene/resources/atlas_texture.cpp @@ -245,11 +245,16 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const { } Ref<Image> AtlasTexture::get_image() const { - if (!atlas.is_valid() || !atlas->get_image().is_valid()) { + if (atlas.is_null() || region.size.x <= 0 || region.size.y <= 0) { return Ref<Image>(); } - return atlas->get_image()->get_region(region); + Ref<Image> atlas_image = atlas->get_image(); + if (atlas_image.is_null()) { + return Ref<Image>(); + } + + return atlas_image->get_region(region); } AtlasTexture::AtlasTexture() {} diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index 5000541621..c2f721a80f 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -132,6 +132,7 @@ void MeshLibrary::create_item(int p_item) { ERR_FAIL_COND(p_item < 0); ERR_FAIL_COND(item_map.has(p_item)); item_map[p_item] = Item(); + emit_changed(); notify_property_list_changed(); } @@ -145,7 +146,6 @@ void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].mesh = p_mesh; emit_changed(); - notify_property_list_changed(); } void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_transform) { @@ -157,7 +157,6 @@ void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_trans void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].shapes = p_shapes; - notify_property_list_changed(); emit_changed(); notify_property_list_changed(); } @@ -165,22 +164,18 @@ void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) void MeshLibrary::set_item_navigation_mesh(int p_item, const Ref<NavigationMesh> &p_navigation_mesh) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].navigation_mesh = p_navigation_mesh; - notify_property_list_changed(); emit_changed(); - notify_property_list_changed(); } void MeshLibrary::set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].navigation_mesh_transform = p_transform; emit_changed(); - notify_property_list_changed(); } void MeshLibrary::set_item_navigation_layers(int p_item, uint32_t p_navigation_layers) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].navigation_layers = p_navigation_layers; - notify_property_list_changed(); emit_changed(); } @@ -188,7 +183,6 @@ void MeshLibrary::set_item_preview(int p_item, const Ref<Texture2D> &p_preview) ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].preview = p_preview; emit_changed(); - notify_property_list_changed(); } String MeshLibrary::get_item_name(int p_item) const { diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 72d38ec8ce..dbb286c6e7 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -762,7 +762,7 @@ void ParticleProcessMaterial::_update_shader() { code += " float base_angle = (tex_angle) * mix(initial_angle_min, initial_angle_max, angle_rand);\n"; code += " base_angle += CUSTOM.y * LIFETIME * (tex_angular_velocity) * mix(angular_velocity_min,angular_velocity_max, rand_from_seed(alt_seed));\n"; code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle - code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, rand_from_seed(alt_seed)) + tv * tex_anim_speed * mix(anim_speed_min, anim_speed_max, rand_from_seed(alt_seed));\n"; // angle + code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, anim_offset_rand) + tv * tex_anim_speed * mix(anim_speed_min, anim_speed_max, rand_from_seed(alt_seed));\n"; // angle // apply color // apply hue rotation diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/servers/rendering/renderer_rd/cluster_builder_rd.cpp index d2a1a5ab9c..64950c6991 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.cpp +++ b/servers/rendering/renderer_rd/cluster_builder_rd.cpp @@ -285,7 +285,7 @@ void ClusterBuilderRD::setup(Size2i p_screen_size, uint32_t p_max_elements, RID cluster_render_buffer = RD::get_singleton()->storage_buffer_create(cluster_render_buffer_size); cluster_buffer = RD::get_singleton()->storage_buffer_create(cluster_buffer_size); - render_elements = static_cast<RenderElementData *>(memalloc(sizeof(RenderElementData *) * render_element_max)); + render_elements = static_cast<RenderElementData *>(memalloc(sizeof(RenderElementData) * render_element_max)); render_element_count = 0; element_buffer = RD::get_singleton()->storage_buffer_create(sizeof(RenderElementData) * render_element_max); diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index d70bff8593..d8c035a51c 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -2427,7 +2427,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["SHADOW_VERTEX"] = "shadow_vertex"; actions.renames["UV"] = "uv"; - actions.renames["POINT_SIZE"] = "gl_PointSize"; + actions.renames["POINT_SIZE"] = "point_size"; actions.renames["MODEL_MATRIX"] = "model_matrix"; actions.renames["CANVAS_MATRIX"] = "canvas_data.canvas_transform"; @@ -2475,6 +2475,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n"; actions.usage_defines["LIGHT"] = "#define LIGHT_SHADER_CODE_USED\n"; actions.usage_defines["SPECULAR_SHININESS"] = "#define SPECULAR_SHININESS_USED\n"; + actions.usage_defines["POINT_SIZE"] = "#define USE_POINT_SIZE\n"; actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index b63eea1401..0283482d76 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -899,6 +899,10 @@ void main() { break; } + if (!bool(decals.data[decal_index].mask & draw_call.layer_mask)) { + continue; //not masked + } + vec3 uv_local = (decals.data[decal_index].xform * vec4(vertex, 1.0)).xyz; if (any(lessThan(uv_local, vec3(0.0, -1.0, 0.0))) || any(greaterThan(uv_local, vec3(1.0)))) { continue; //out of decal diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index e3e86acb7a..6fc13867cc 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -303,6 +303,11 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_shaped_text_next_grapheme_pos, "shaped", "pos"); GDVIRTUAL_BIND(_shaped_text_prev_grapheme_pos, "shaped", "pos"); + GDVIRTUAL_BIND(_shaped_text_get_character_breaks, "shaped"); + GDVIRTUAL_BIND(_shaped_text_next_character_pos, "shaped", "pos"); + GDVIRTUAL_BIND(_shaped_text_prev_character_pos, "shaped", "pos"); + GDVIRTUAL_BIND(_shaped_text_closest_character_pos, "shaped", "pos"); + GDVIRTUAL_BIND(_format_number, "string", "language"); GDVIRTUAL_BIND(_parse_number, "string", "language"); GDVIRTUAL_BIND(_percent_sign, "language"); @@ -311,6 +316,7 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_is_valid_identifier, "string"); GDVIRTUAL_BIND(_string_get_word_breaks, "string", "language", "chars_per_line"); + GDVIRTUAL_BIND(_string_get_character_breaks, "string", "language"); GDVIRTUAL_BIND(_is_confusable, "string", "dict"); GDVIRTUAL_BIND(_spoof_check, "string"); @@ -1333,6 +1339,38 @@ int64_t TextServerExtension::shaped_text_prev_grapheme_pos(const RID &p_shaped, return TextServer::shaped_text_prev_grapheme_pos(p_shaped, p_pos); } +PackedInt32Array TextServerExtension::shaped_text_get_character_breaks(const RID &p_shaped) const { + PackedInt32Array ret; + if (GDVIRTUAL_CALL(_shaped_text_get_character_breaks, p_shaped, ret)) { + return ret; + } + return PackedInt32Array(); +} + +int64_t TextServerExtension::shaped_text_next_character_pos(const RID &p_shaped, int64_t p_pos) const { + int64_t ret; + if (GDVIRTUAL_CALL(_shaped_text_next_character_pos, p_shaped, p_pos, ret)) { + return ret; + } + return TextServer::shaped_text_next_character_pos(p_shaped, p_pos); +} + +int64_t TextServerExtension::shaped_text_prev_character_pos(const RID &p_shaped, int64_t p_pos) const { + int64_t ret; + if (GDVIRTUAL_CALL(_shaped_text_prev_character_pos, p_shaped, p_pos, ret)) { + return ret; + } + return TextServer::shaped_text_prev_character_pos(p_shaped, p_pos); +} + +int64_t TextServerExtension::shaped_text_closest_character_pos(const RID &p_shaped, int64_t p_pos) const { + int64_t ret; + if (GDVIRTUAL_CALL(_shaped_text_closest_character_pos, p_shaped, p_pos, ret)) { + return ret; + } + return TextServer::shaped_text_closest_character_pos(p_shaped, p_pos); +} + String TextServerExtension::format_number(const String &p_string, const String &p_language) const { String ret; if (GDVIRTUAL_CALL(_format_number, p_string, p_language, ret)) { @@ -1399,6 +1437,14 @@ PackedInt32Array TextServerExtension::string_get_word_breaks(const String &p_str return ret; } +PackedInt32Array TextServerExtension::string_get_character_breaks(const String &p_string, const String &p_language) const { + PackedInt32Array ret; + if (GDVIRTUAL_CALL(_string_get_character_breaks, p_string, p_language, ret)) { + return ret; + } + return TextServer::string_get_character_breaks(p_string, p_language); +} + int64_t TextServerExtension::is_confusable(const String &p_string, const PackedStringArray &p_dict) const { int64_t ret; if (GDVIRTUAL_CALL(_is_confusable, p_string, p_dict, ret)) { diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index cfaec7e629..a0c47a70d6 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -505,6 +505,15 @@ public: GDVIRTUAL2RC(int64_t, _shaped_text_next_grapheme_pos, RID, int64_t); GDVIRTUAL2RC(int64_t, _shaped_text_prev_grapheme_pos, RID, int64_t); + virtual PackedInt32Array shaped_text_get_character_breaks(const RID &p_shaped) const override; + virtual int64_t shaped_text_next_character_pos(const RID &p_shaped, int64_t p_pos) const override; + virtual int64_t shaped_text_prev_character_pos(const RID &p_shaped, int64_t p_pos) const override; + virtual int64_t shaped_text_closest_character_pos(const RID &p_shaped, int64_t p_pos) const override; + GDVIRTUAL1RC(PackedInt32Array, _shaped_text_get_character_breaks, RID); + GDVIRTUAL2RC(int64_t, _shaped_text_next_character_pos, RID, int64_t); + GDVIRTUAL2RC(int64_t, _shaped_text_prev_character_pos, RID, int64_t); + GDVIRTUAL2RC(int64_t, _shaped_text_closest_character_pos, RID, int64_t); + virtual String format_number(const String &p_string, const String &p_language = "") const override; virtual String parse_number(const String &p_string, const String &p_language = "") const override; virtual String percent_sign(const String &p_language = "") const override; @@ -518,6 +527,9 @@ public: virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "", int64_t p_chars_per_line = 0) const override; GDVIRTUAL3RC(PackedInt32Array, _string_get_word_breaks, const String &, const String &, int64_t); + virtual PackedInt32Array string_get_character_breaks(const String &p_string, const String &p_language = "") const override; + GDVIRTUAL2RC(PackedInt32Array, _string_get_character_breaks, const String &, const String &); + virtual bool is_valid_identifier(const String &p_string) const override; GDVIRTUAL1RC(bool, _is_valid_identifier, const String &); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 07ad14f120..28c6b20f7b 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -448,6 +448,11 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_next_grapheme_pos", "shaped", "pos"), &TextServer::shaped_text_next_grapheme_pos); ClassDB::bind_method(D_METHOD("shaped_text_prev_grapheme_pos", "shaped", "pos"), &TextServer::shaped_text_prev_grapheme_pos); + ClassDB::bind_method(D_METHOD("shaped_text_get_character_breaks", "shaped"), &TextServer::shaped_text_get_character_breaks); + ClassDB::bind_method(D_METHOD("shaped_text_next_character_pos", "shaped", "pos"), &TextServer::shaped_text_next_character_pos); + ClassDB::bind_method(D_METHOD("shaped_text_prev_character_pos", "shaped", "pos"), &TextServer::shaped_text_prev_character_pos); + ClassDB::bind_method(D_METHOD("shaped_text_closest_character_pos", "shaped", "pos"), &TextServer::shaped_text_closest_character_pos); + ClassDB::bind_method(D_METHOD("shaped_text_draw", "shaped", "canvas", "pos", "clip_l", "clip_r", "color"), &TextServer::shaped_text_draw, DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("shaped_text_draw_outline", "shaped", "canvas", "pos", "clip_l", "clip_r", "outline_size", "color"), &TextServer::shaped_text_draw_outline, DEFVAL(-1), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1, 1, 1))); @@ -458,6 +463,7 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("percent_sign", "language"), &TextServer::percent_sign, DEFVAL("")); ClassDB::bind_method(D_METHOD("string_get_word_breaks", "string", "language", "chars_per_line"), &TextServer::string_get_word_breaks, DEFVAL(""), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("string_get_character_breaks", "string", "language"), &TextServer::string_get_character_breaks, DEFVAL("")); ClassDB::bind_method(D_METHOD("is_confusable", "string", "dict"), &TextServer::is_confusable); ClassDB::bind_method(D_METHOD("spoof_check", "string"), &TextServer::spoof_check); @@ -1424,6 +1430,59 @@ int64_t TextServer::shaped_text_prev_grapheme_pos(const RID &p_shaped, int64_t p return p_pos; } +int64_t TextServer::shaped_text_prev_character_pos(const RID &p_shaped, int64_t p_pos) const { + const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped); + int64_t prev = 0; + for (const int32_t &E : chars) { + if (E >= p_pos) { + return prev; + } + prev = E; + } + return prev; +} + +int64_t TextServer::shaped_text_next_character_pos(const RID &p_shaped, int64_t p_pos) const { + const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped); + int64_t prev = 0; + for (const int32_t &E : chars) { + if (E > p_pos) { + return E; + } + prev = E; + } + return prev; +} + +int64_t TextServer::shaped_text_closest_character_pos(const RID &p_shaped, int64_t p_pos) const { + const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped); + int64_t prev = 0; + for (const int32_t &E : chars) { + if (E == p_pos) { + return E; + } else if (E > p_pos) { + if ((E - p_pos) < (p_pos - prev)) { + return E; + } else { + return prev; + } + } + prev = E; + } + return prev; +} + +PackedInt32Array TextServer::string_get_character_breaks(const String &p_string, const String &p_language) const { + PackedInt32Array ret; + if (!p_string.is_empty()) { + ret.resize(p_string.size() - 1); + for (int i = 0; i < p_string.size() - 1; i++) { + ret.write[i] = i + 1; + } + } + return ret; +} + void TextServer::shaped_text_draw(const RID &p_shaped, const RID &p_canvas, const Vector2 &p_pos, double p_clip_l, double p_clip_r, const Color &p_color) const { TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); bool hex_codes = shaped_text_get_preserve_control(p_shaped) || shaped_text_get_preserve_invalid(p_shaped); diff --git a/servers/text_server.h b/servers/text_server.h index a4ea1dfdc7..7bc353cb8c 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -491,6 +491,11 @@ public: virtual int64_t shaped_text_next_grapheme_pos(const RID &p_shaped, int64_t p_pos) const; virtual int64_t shaped_text_prev_grapheme_pos(const RID &p_shaped, int64_t p_pos) const; + virtual PackedInt32Array shaped_text_get_character_breaks(const RID &p_shaped) const = 0; + virtual int64_t shaped_text_next_character_pos(const RID &p_shaped, int64_t p_pos) const; + virtual int64_t shaped_text_prev_character_pos(const RID &p_shaped, int64_t p_pos) const; + virtual int64_t shaped_text_closest_character_pos(const RID &p_shaped, int64_t p_pos) const; + // The pen position is always placed on the baseline and moveing left to right. virtual void shaped_text_draw(const RID &p_shaped, const RID &p_canvas, const Vector2 &p_pos, double p_clip_l = -1.0, double p_clip_r = -1.0, const Color &p_color = Color(1, 1, 1)) const; virtual void shaped_text_draw_outline(const RID &p_shaped, const RID &p_canvas, const Vector2 &p_pos, double p_clip_l = -1.0, double p_clip_r = -1.0, int64_t p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const; @@ -502,6 +507,7 @@ public: // String functions. virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "", int64_t p_chars_per_line = 0) const = 0; + virtual PackedInt32Array string_get_character_breaks(const String &p_string, const String &p_language = "") const; virtual int64_t is_confusable(const String &p_string, const PackedStringArray &p_dict) const { return -1; }; virtual bool spoof_check(const String &p_string) const { return false; }; diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h index 44275285c8..b42cb37234 100644 --- a/servers/xr/xr_interface.h +++ b/servers/xr/xr_interface.h @@ -52,7 +52,7 @@ class XRInterface : public RefCounted { GDCLASS(XRInterface, RefCounted); public: - enum Capabilities { /* purely meta data, provides some info about what this interface supports */ + enum Capabilities { /* purely metadata, provides some info about what this interface supports */ XR_NONE = 0, /* no capabilities */ XR_MONO = 1, /* can be used with mono output */ XR_STEREO = 2, /* can be used with stereo output */ diff --git a/tests/scene/test_arraymesh.h b/tests/scene/test_arraymesh.h index b2a2ecc3bf..0e97e7d75f 100644 --- a/tests/scene/test_arraymesh.h +++ b/tests/scene/test_arraymesh.h @@ -154,7 +154,7 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") { } } -TEST_CASE("[SceneTree][ArrayMesh] Surface meta data tests.") { +TEST_CASE("[SceneTree][ArrayMesh] Surface metadata tests.") { Ref<ArrayMesh> mesh = memnew(ArrayMesh); Ref<CylinderMesh> cylinder = memnew(CylinderMesh); Array cylinder_array{}; diff --git a/tests/scene/test_control.h b/tests/scene/test_control.h new file mode 100644 index 0000000000..3d7e389e0a --- /dev/null +++ b/tests/scene/test_control.h @@ -0,0 +1,66 @@ +/**************************************************************************/ +/* test_control.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 TEST_CONTROL_H +#define TEST_CONTROL_H + +#include "scene/gui/control.h" + +#include "tests/test_macros.h" + +namespace TestControl { + +TEST_CASE("[SceneTree][Control]") { + SUBCASE("[Control][Global Transform] Global Transform should be accessible while not in SceneTree.") { // GH-79453 + Control *test_node = memnew(Control); + Control *test_child = memnew(Control); + test_node->add_child(test_child); + + test_node->set_global_position(Point2(1, 1)); + CHECK_EQ(test_node->get_global_position(), Point2(1, 1)); + CHECK_EQ(test_child->get_global_position(), Point2(1, 1)); + test_node->set_global_position(Point2(2, 2)); + CHECK_EQ(test_node->get_global_position(), Point2(2, 2)); + test_node->set_scale(Vector2(4, 4)); + CHECK_EQ(test_node->get_global_transform(), Transform2D(0, Size2(4, 4), 0, Vector2(2, 2))); + test_node->set_scale(Vector2(1, 1)); + test_node->set_rotation_degrees(90); + CHECK_EQ(test_node->get_global_transform(), Transform2D(Math_PI / 2, Vector2(2, 2))); + test_node->set_pivot_offset(Vector2(1, 0)); + CHECK_EQ(test_node->get_global_transform(), Transform2D(Math_PI / 2, Vector2(3, 1))); + + memdelete(test_child); + memdelete(test_node); + } +} + +} // namespace TestControl + +#endif // TEST_CONTROL_H diff --git a/tests/scene/test_node_2d.h b/tests/scene/test_node_2d.h new file mode 100644 index 0000000000..7e93c77e22 --- /dev/null +++ b/tests/scene/test_node_2d.h @@ -0,0 +1,63 @@ +/**************************************************************************/ +/* test_node_2d.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 TEST_NODE_2D_H +#define TEST_NODE_2D_H + +#include "scene/2d/node_2d.h" + +#include "tests/test_macros.h" + +namespace TestNode2D { + +TEST_CASE("[SceneTree][Node2D]") { + SUBCASE("[Node2D][Global Transform] Global Transform should be accessible while not in SceneTree.") { // GH-79453 + Node2D *test_node = memnew(Node2D); + test_node->set_name("node"); + Node2D *test_child = memnew(Node2D); + test_child->set_name("child"); + test_node->add_child(test_child); + + test_node->set_global_position(Point2(1, 1)); + CHECK_EQ(test_node->get_global_position(), Point2(1, 1)); + CHECK_EQ(test_child->get_global_position(), Point2(1, 1)); + test_node->set_global_position(Point2(2, 2)); + CHECK_EQ(test_node->get_global_position(), Point2(2, 2)); + test_node->set_global_transform(Transform2D(0, Point2(3, 3))); + CHECK_EQ(test_node->get_global_position(), Point2(3, 3)); + + memdelete(test_child); + memdelete(test_node); + } +} + +} // namespace TestNode2D + +#endif // TEST_NODE_2D_H diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 8cfb189370..79766cd919 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -2297,34 +2297,6 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); - - text_edit->set_caret_mid_grapheme_enabled(false); - CHECK_FALSE(text_edit->is_caret_mid_grapheme_enabled()); - - text_edit->start_action(TextEdit::EditAction::ACTION_NONE); - - text_edit->undo(); - MessageQueue::get_singleton()->flush(); - CHECK(text_edit->get_text() == "ffi some test text.ffi some test text."); - - SIGNAL_DISCARD("text_set"); - SIGNAL_DISCARD("text_changed"); - SIGNAL_DISCARD("lines_edited_from"); - SIGNAL_DISCARD("caret_changed"); - - SEND_GUI_ACTION("ui_text_delete"); - CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " some test text. some test text."); - CHECK(text_edit->get_caret_line() == 0); - CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection(0)); - - CHECK(text_edit->get_caret_line(1) == 0); - CHECK(text_edit->get_caret_column(1) == 16); - CHECK_FALSE(text_edit->has_selection(1)); - SIGNAL_CHECK("caret_changed", empty_signal_args); - SIGNAL_CHECK("text_changed", empty_signal_args); - SIGNAL_CHECK("lines_edited_from", lines_edited_args); } SUBCASE("[TextEdit] ui_text_caret_word_left") { @@ -3335,18 +3307,6 @@ TEST_CASE("[SceneTree][TextEdit] caret") { SEND_GUI_ACTION("ui_text_caret_left"); CHECK(text_edit->get_caret_column() == 2); - text_edit->set_caret_mid_grapheme_enabled(false); - CHECK_FALSE(text_edit->is_caret_mid_grapheme_enabled()); - - SEND_GUI_ACTION("ui_text_caret_left"); - CHECK(text_edit->get_caret_column() == 0); - - SEND_GUI_ACTION("ui_text_caret_right"); - CHECK(text_edit->get_caret_column() == 3); - - SEND_GUI_ACTION("ui_text_caret_left"); - CHECK(text_edit->get_caret_column() == 0); - text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); for (int i = 0; i < 3; i++) { text_edit->insert_line_at(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h index a116559cb2..691536da8e 100644 --- a/tests/servers/test_navigation_server_3d.h +++ b/tests/servers/test_navigation_server_3d.h @@ -123,6 +123,7 @@ TEST_SUITE("[Navigation]") { CHECK_FALSE(map.is_valid()); SUBCASE("Queries against invalid map should return empty or invalid values") { + ERR_PRINT_OFF; CHECK_EQ(navigation_server->map_get_closest_point(map, Vector3(7, 7, 7)), Vector3()); CHECK_EQ(navigation_server->map_get_closest_point_normal(map, Vector3(7, 7, 7)), Vector3()); CHECK_FALSE(navigation_server->map_get_closest_point_owner(map, Vector3(7, 7, 7)).is_valid()); @@ -141,6 +142,7 @@ TEST_SUITE("[Navigation]") { CHECK_EQ(query_result->get_path_types().size(), 0); CHECK_EQ(query_result->get_path_rids().size(), 0); CHECK_EQ(query_result->get_path_owner_ids().size(), 0); + ERR_PRINT_ON; } map = navigation_server->map_create(); @@ -225,6 +227,7 @@ TEST_SUITE("[Navigation]") { navigation_server->map_set_active(map, true); navigation_server->process(0.0); // Give server some cycles to commit. + ERR_PRINT_OFF; CHECK_EQ(navigation_server->map_get_closest_point(map, Vector3(7, 7, 7)), Vector3()); CHECK_EQ(navigation_server->map_get_closest_point_normal(map, Vector3(7, 7, 7)), Vector3()); CHECK_FALSE(navigation_server->map_get_closest_point_owner(map, Vector3(7, 7, 7)).is_valid()); @@ -243,6 +246,7 @@ TEST_SUITE("[Navigation]") { CHECK_EQ(query_result->get_path_types().size(), 0); CHECK_EQ(query_result->get_path_rids().size(), 0); CHECK_EQ(query_result->get_path_owner_ids().size(), 0); + ERR_PRINT_ON; navigation_server->map_set_active(map, false); navigation_server->process(0.0); // Give server some cycles to commit. @@ -350,9 +354,11 @@ TEST_SUITE("[Navigation]") { } SUBCASE("Queries against empty region should return empty or invalid values") { + ERR_PRINT_OFF; CHECK_EQ(navigation_server->region_get_connections_count(region), 0); CHECK_EQ(navigation_server->region_get_connection_pathway_end(region, 55), Vector3()); CHECK_EQ(navigation_server->region_get_connection_pathway_start(region, 55), Vector3()); + ERR_PRINT_ON; } navigation_server->free(region); @@ -450,7 +456,9 @@ TEST_SUITE("[Navigation]") { CHECK_EQ(navigation_mesh->get_polygon_count(), 0); CHECK_EQ(navigation_mesh->get_vertices().size(), 0); + ERR_PRINT_OFF; navigation_server->region_bake_navigation_mesh(navigation_mesh, node_3d); + ERR_PRINT_ON; // FIXME: The above line should trigger the update (line below) under the hood. navigation_server->region_set_navigation_mesh(region, navigation_mesh); // Force update. CHECK_EQ(navigation_mesh->get_polygon_count(), 2); diff --git a/tests/servers/test_text_server.h b/tests/servers/test_text_server.h index 989d83d504..eef5b850ca 100644 --- a/tests/servers/test_text_server.h +++ b/tests/servers/test_text_server.h @@ -175,10 +175,13 @@ TEST_SUITE("[TextServer]") { RID font1 = ts->create_font(); ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size); + ts->font_set_allow_system_fallback(font1, false); RID font2 = ts->create_font(); ts->font_set_data_ptr(font2, _font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size); + ts->font_set_allow_system_fallback(font2, false); RID font3 = ts->create_font(); ts->font_set_data_ptr(font3, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size); + ts->font_set_allow_system_fallback(font3, false); Array font; font.push_back(font1); @@ -186,6 +189,83 @@ TEST_SUITE("[TextServer]") { font.push_back(font3); { + RID ctx = ts->create_shaped_text(); + CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed."); + ts->shaped_text_add_string(ctx, U"Xtest", font, 10); + ts->shaped_text_add_string(ctx, U"xs", font, 10); + RID sctx = ts->shaped_text_substr(ctx, 1, 5); + CHECK_FALSE_MESSAGE(sctx == RID(), "Creating substring text buffer failed."); + PackedInt32Array sbrk = ts->shaped_text_get_character_breaks(sctx); + CHECK_FALSE_MESSAGE(sbrk.size() != 5, "Invalid substring char breaks number."); + if (sbrk.size() == 5) { + CHECK_FALSE_MESSAGE(sbrk[0] != 2, "Invalid substring char break position."); + CHECK_FALSE_MESSAGE(sbrk[1] != 3, "Invalid substring char break position."); + CHECK_FALSE_MESSAGE(sbrk[2] != 4, "Invalid substring char break position."); + CHECK_FALSE_MESSAGE(sbrk[3] != 5, "Invalid substring char break position."); + CHECK_FALSE_MESSAGE(sbrk[4] != 6, "Invalid substring char break position."); + } + PackedInt32Array fbrk = ts->shaped_text_get_character_breaks(ctx); + CHECK_FALSE_MESSAGE(fbrk.size() != 7, "Invalid char breaks number."); + if (fbrk.size() == 7) { + CHECK_FALSE_MESSAGE(fbrk[0] != 1, "Invalid char break position."); + CHECK_FALSE_MESSAGE(fbrk[1] != 2, "Invalid char break position."); + CHECK_FALSE_MESSAGE(fbrk[2] != 3, "Invalid char break position."); + CHECK_FALSE_MESSAGE(fbrk[3] != 4, "Invalid char break position."); + CHECK_FALSE_MESSAGE(fbrk[4] != 5, "Invalid char break position."); + CHECK_FALSE_MESSAGE(fbrk[5] != 6, "Invalid char break position."); + CHECK_FALSE_MESSAGE(fbrk[6] != 7, "Invalid char break position."); + } + PackedInt32Array rbrk = ts->string_get_character_breaks(U"Xtestxs"); + CHECK_FALSE_MESSAGE(rbrk.size() != 7, "Invalid char breaks number."); + if (rbrk.size() == 7) { + CHECK_FALSE_MESSAGE(rbrk[0] != 1, "Invalid char break position."); + CHECK_FALSE_MESSAGE(rbrk[1] != 2, "Invalid char break position."); + CHECK_FALSE_MESSAGE(rbrk[2] != 3, "Invalid char break position."); + CHECK_FALSE_MESSAGE(rbrk[3] != 4, "Invalid char break position."); + CHECK_FALSE_MESSAGE(rbrk[4] != 5, "Invalid char break position."); + CHECK_FALSE_MESSAGE(rbrk[5] != 6, "Invalid char break position."); + CHECK_FALSE_MESSAGE(rbrk[6] != 7, "Invalid char break position."); + } + + ts->free_rid(sctx); + ts->free_rid(ctx); + } + + if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) { + RID ctx = ts->create_shaped_text(); + CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed."); + ts->shaped_text_add_string(ctx, U"X❤️🔥", font, 10); + ts->shaped_text_add_string(ctx, U"xs", font, 10); + RID sctx = ts->shaped_text_substr(ctx, 1, 5); + CHECK_FALSE_MESSAGE(sctx == RID(), "Creating substring text buffer failed."); + PackedInt32Array sbrk = ts->shaped_text_get_character_breaks(sctx); + CHECK_FALSE_MESSAGE(sbrk.size() != 2, "Invalid substring char breaks number."); + if (sbrk.size() == 2) { + CHECK_FALSE_MESSAGE(sbrk[0] != 5, "Invalid substring char break position."); + CHECK_FALSE_MESSAGE(sbrk[1] != 6, "Invalid substring char break position."); + } + PackedInt32Array fbrk = ts->shaped_text_get_character_breaks(ctx); + CHECK_FALSE_MESSAGE(fbrk.size() != 4, "Invalid char breaks number."); + if (fbrk.size() == 4) { + CHECK_FALSE_MESSAGE(fbrk[0] != 1, "Invalid char break position."); + CHECK_FALSE_MESSAGE(fbrk[1] != 5, "Invalid char break position."); + CHECK_FALSE_MESSAGE(fbrk[2] != 6, "Invalid char break position."); + CHECK_FALSE_MESSAGE(fbrk[3] != 7, "Invalid char break position."); + } + PackedInt32Array rbrk = ts->string_get_character_breaks(U"X❤️🔥xs"); + CHECK_FALSE_MESSAGE(rbrk.size() != 4, "Invalid char breaks number."); + if (rbrk.size() == 4) { + CHECK_FALSE_MESSAGE(rbrk[0] != 1, "Invalid char break position."); + CHECK_FALSE_MESSAGE(rbrk[1] != 5, "Invalid char break position."); + CHECK_FALSE_MESSAGE(rbrk[2] != 6, "Invalid char break position."); + CHECK_FALSE_MESSAGE(rbrk[3] != 7, "Invalid char break position."); + } + + ts->free_rid(sctx); + ts->free_rid(ctx); + } + + { String test = U"Test test long text long text\n"; RID ctx = ts->create_shaped_text(); CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed."); @@ -636,6 +716,54 @@ TEST_SUITE("[TextServer]") { CHECK(breaks[17] == 42); } } + + if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) { + String text2 = U"U+2764 U+FE0F U+200D U+1F525 ; 13.1 # ❤️🔥"; + + PackedInt32Array breaks = ts->string_get_character_breaks(text2, "en"); + CHECK(breaks.size() == 39); + if (breaks.size() == 39) { + CHECK(breaks[0] == 1); + CHECK(breaks[1] == 2); + CHECK(breaks[2] == 3); + CHECK(breaks[3] == 4); + CHECK(breaks[4] == 5); + CHECK(breaks[5] == 6); + CHECK(breaks[6] == 7); + CHECK(breaks[7] == 8); + CHECK(breaks[8] == 9); + CHECK(breaks[9] == 10); + CHECK(breaks[10] == 11); + CHECK(breaks[11] == 12); + CHECK(breaks[12] == 13); + CHECK(breaks[13] == 14); + CHECK(breaks[14] == 15); + CHECK(breaks[15] == 16); + CHECK(breaks[16] == 17); + CHECK(breaks[17] == 18); + CHECK(breaks[18] == 19); + CHECK(breaks[19] == 20); + CHECK(breaks[20] == 21); + CHECK(breaks[21] == 22); + CHECK(breaks[22] == 23); + CHECK(breaks[23] == 24); + CHECK(breaks[24] == 25); + CHECK(breaks[25] == 26); + CHECK(breaks[26] == 27); + CHECK(breaks[27] == 28); + CHECK(breaks[28] == 29); + CHECK(breaks[29] == 30); + CHECK(breaks[30] == 31); + CHECK(breaks[31] == 32); + CHECK(breaks[32] == 33); + CHECK(breaks[33] == 34); + CHECK(breaks[34] == 35); + CHECK(breaks[35] == 36); + CHECK(breaks[36] == 37); + CHECK(breaks[37] == 38); + CHECK(breaks[38] == 42); + } + } } } } diff --git a/tests/test_main.cpp b/tests/test_main.cpp index f1e348345b..6cc7aad48e 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -95,6 +95,7 @@ #include "tests/scene/test_bit_map.h" #include "tests/scene/test_code_edit.h" #include "tests/scene/test_color_picker.h" +#include "tests/scene/test_control.h" #include "tests/scene/test_curve.h" #include "tests/scene/test_curve_2d.h" #include "tests/scene/test_curve_3d.h" @@ -106,6 +107,7 @@ #include "tests/scene/test_navigation_region_2d.h" #include "tests/scene/test_navigation_region_3d.h" #include "tests/scene/test_node.h" +#include "tests/scene/test_node_2d.h" #include "tests/scene/test_packed_scene.h" #include "tests/scene/test_path_2d.h" #include "tests/scene/test_path_3d.h" @@ -242,8 +244,10 @@ struct GodotTestCaseListener : public doctest::IReporter { physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server(); physics_server_2d->init(); + ERR_PRINT_OFF; navigation_server_3d = NavigationServer3DManager::new_default_server(); navigation_server_2d = memnew(NavigationServer2D); + ERR_PRINT_ON; memnew(InputMap); InputMap::get_singleton()->load_default(); @@ -269,8 +273,10 @@ struct GodotTestCaseListener : public doctest::IReporter { } if (suite_name.find("[Navigation]") != -1 && navigation_server_2d == nullptr && navigation_server_3d == nullptr) { + ERR_PRINT_OFF; navigation_server_3d = NavigationServer3DManager::new_default_server(); navigation_server_2d = memnew(NavigationServer2D); + ERR_PRINT_ON; return; } } diff --git a/thirdparty/README.md b/thirdparty/README.md index 5053502971..f298a3a16c 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -299,6 +299,24 @@ Files extracted from upstream source: - `jpge*.{c,h}` +## libktx + +- Upstream: https://github.com/KhronosGroup/KTX-Software +- Version: 4.1.0 (d7255fe73cd53b856731ceb9f2c279181d0dbbca, 2023) +- License: Apache-2.0 + +Files extracted from upstream source: + +- `LICENSE.md` +- `include/*` +- `lib/dfdutils/{LICENSES/Apache-2.0.txt,KHR,*.c,*.h,*.inl}` +- `lib/{basis_sgd.h,basis_transcode.cpp,checkheader.c,filestream.*,formatsize.h,gl_format.h,hashlist.c,ktxint.h,memstream.*,swap.c,texture*,uthash.h,vk_format.h,vkformat_enum.h}` +- `utils/unused.h` +- `other_include/KHR/*` + +Some Godot-specific changes are applied via `godot.patch`. + + ## libogg - Upstream: https://www.xiph.org/ogg diff --git a/thirdparty/libktx/Apache-2.0.txt b/thirdparty/libktx/Apache-2.0.txt new file mode 100644 index 0000000000..4ed90b9522 --- /dev/null +++ b/thirdparty/libktx/Apache-2.0.txt @@ -0,0 +1,208 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, +AND DISTRIBUTION + + 1. Definitions. + + + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + + + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + + + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + + + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + + + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + + + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. diff --git a/thirdparty/libktx/LICENSE.dfdutils.adoc b/thirdparty/libktx/LICENSE.dfdutils.adoc new file mode 100644 index 0000000000..f206249940 --- /dev/null +++ b/thirdparty/libktx/LICENSE.dfdutils.adoc @@ -0,0 +1,10 @@ += LICENSE file for the KhronosGroup/dfdutils project + +Files in this repository fall under this license: + + * SPDX license identifier: "`Apache-2.0`" + ** Apache License 2.0 + +Full license text is available at: + + * Apache-2.0: https://opensource.org/licenses/Apache-2.0 diff --git a/thirdparty/libktx/LICENSE.md b/thirdparty/libktx/LICENSE.md new file mode 100644 index 0000000000..7b6d05fe48 --- /dev/null +++ b/thirdparty/libktx/LICENSE.md @@ -0,0 +1,36 @@ +LICENSE file for the KhronosGroup/KTX-Software project {#license} +====================================================== + +<!-- + Can't put at start. Doxygen requires page title on first line. + Copyright 2013-2020 Mark Callow + SPDX-License-Identifier: Apache-2.0 +--> + +Files unique to this repository generally fall under the Apache 2.0 license +with copyright holders including Mark Callow, the KTX-Software author; The +Khronos Group Inc., which has supported KTX development; and other +contributors to the KTX project. + +Because KTX-Software incorporates material and contributions from many other +projects, which often have their own licenses, there are many other licenses +in use in this repository. While there are many licenses in this repository, +with rare exceptions all are open source licenses that we believe to be +mutually compatible. + +The complete text of each of the licenses used in this repository is found +in LICENSES/*.txt . Additionally, we have updated the repository to pass the +REUSE compliance checker tool (see https://reuse.software/). REUSE verifies +that every file in a git repository either incorporates a license, or that +the license is present in auxiliary files such as .reuse/dep5 . To obtain a +bill of materials for the repository identifying the license for each file, +install the REUSE tool and run + + reuse spdx + +inside the repository. + +## Special Cases + +The file lib/etcdec.cxx is not open source. It is made available under the +terms of an Ericsson license, found in the file itself. diff --git a/thirdparty/libktx/godot.patch b/thirdparty/libktx/godot.patch new file mode 100644 index 0000000000..8a492ee27d --- /dev/null +++ b/thirdparty/libktx/godot.patch @@ -0,0 +1,45 @@ +--- thirdparty/libktx/lib/gl_format.h ++++ thirdparty/libktx/lib/gl_format.h +@@ -92,7 +92,9 @@ + #include "vkformat_enum.h" + + #if defined(_WIN32) && !defined(__MINGW32__) ++#ifndef NOMINMAX + #define NOMINMAX ++#endif + #ifndef __cplusplus + #undef inline + #define inline __inline +--- thirdparty/libktx/lib/basis_transcode.cpp ++++ thirdparty/libktx/lib/basis_transcode.cpp +@@ -29,9 +29,9 @@ + #include "vkformat_enum.h" + #include "vk_format.h" + #include "basis_sgd.h" +-#include "basisu/transcoder/basisu_file_headers.h" +-#include "basisu/transcoder/basisu_transcoder.h" +-#include "basisu/transcoder/basisu_transcoder_internal.h" ++#include "transcoder/basisu_file_headers.h" ++#include "transcoder/basisu_transcoder.h" ++#include "transcoder/basisu_transcoder_internal.h" + + #undef DECLARE_PRIVATE + #undef DECLARE_PROTECTED +--- thirdparty/libktx/lib/dfdutils/vk2dfd.inl ++++ thirdparty/libktx/lib/dfdutils/vk2dfd.inl +@@ -298,6 +298,7 @@ + case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 10, 1, s_SFLOAT); + case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 12, 10, 1, s_SFLOAT); + case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 12, 12, 1, s_SFLOAT); ++#if 0 + case VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_UNORM); + case VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_SRGB); + case VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_SFLOAT); +@@ -328,6 +329,7 @@ + case VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_UNORM); + case VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_SRGB); + case VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_SFLOAT); ++#endif + case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT: { + int channels[] = {2,1,0,3}; int bits[] = {4,4,4,4}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); diff --git a/thirdparty/libktx/include/KHR/khr_df.h b/thirdparty/libktx/include/KHR/khr_df.h new file mode 100644 index 0000000000..bbd0d14bd9 --- /dev/null +++ b/thirdparty/libktx/include/KHR/khr_df.h @@ -0,0 +1,619 @@ +/* The Khronos Data Format Specification (version 1.3) */ +/* +** Copyright 2015-2020 The Khronos Group Inc. +** SPDX-License-Identifier: Apache-2.0 +*/ + +/* This header defines a structure that can describe the layout of image + formats in memory. This means that the data format is transparent to + the application, and the expectation is that this should be used when + the layout is defined external to the API. Many Khronos APIs deliberately + keep the internal layout of images opaque, to allow proprietary layouts + and optimisations. This structure is not appropriate for describing + opaque layouts. */ + +/* We stick to standard C89 constructs for simplicity and portability. */ + +#ifndef _KHR_DATA_FORMAT_H_ +#define _KHR_DATA_FORMAT_H_ + +/* Accessors */ +typedef enum _khr_word_e { + KHR_DF_WORD_VENDORID = 0U, + KHR_DF_WORD_DESCRIPTORTYPE = 0U, + KHR_DF_WORD_VERSIONNUMBER = 1U, + KHR_DF_WORD_DESCRIPTORBLOCKSIZE = 1U, + KHR_DF_WORD_MODEL = 2U, + KHR_DF_WORD_PRIMARIES = 2U, + KHR_DF_WORD_TRANSFER = 2U, + KHR_DF_WORD_FLAGS = 2U, + KHR_DF_WORD_TEXELBLOCKDIMENSION0 = 3U, + KHR_DF_WORD_TEXELBLOCKDIMENSION1 = 3U, + KHR_DF_WORD_TEXELBLOCKDIMENSION2 = 3U, + KHR_DF_WORD_TEXELBLOCKDIMENSION3 = 3U, + KHR_DF_WORD_BYTESPLANE0 = 4U, + KHR_DF_WORD_BYTESPLANE1 = 4U, + KHR_DF_WORD_BYTESPLANE2 = 4U, + KHR_DF_WORD_BYTESPLANE3 = 4U, + KHR_DF_WORD_BYTESPLANE4 = 5U, + KHR_DF_WORD_BYTESPLANE5 = 5U, + KHR_DF_WORD_BYTESPLANE6 = 5U, + KHR_DF_WORD_BYTESPLANE7 = 5U, + KHR_DF_WORD_SAMPLESTART = 6U, + KHR_DF_WORD_SAMPLEWORDS = 4U +} khr_df_word_e; + +typedef enum _khr_df_shift_e { + KHR_DF_SHIFT_VENDORID = 0U, + KHR_DF_SHIFT_DESCRIPTORTYPE = 17U, + KHR_DF_SHIFT_VERSIONNUMBER = 0U, + KHR_DF_SHIFT_DESCRIPTORBLOCKSIZE = 16U, + KHR_DF_SHIFT_MODEL = 0U, + KHR_DF_SHIFT_PRIMARIES = 8U, + KHR_DF_SHIFT_TRANSFER = 16U, + KHR_DF_SHIFT_FLAGS = 24U, + KHR_DF_SHIFT_TEXELBLOCKDIMENSION0 = 0U, + KHR_DF_SHIFT_TEXELBLOCKDIMENSION1 = 8U, + KHR_DF_SHIFT_TEXELBLOCKDIMENSION2 = 16U, + KHR_DF_SHIFT_TEXELBLOCKDIMENSION3 = 24U, + KHR_DF_SHIFT_BYTESPLANE0 = 0U, + KHR_DF_SHIFT_BYTESPLANE1 = 8U, + KHR_DF_SHIFT_BYTESPLANE2 = 16U, + KHR_DF_SHIFT_BYTESPLANE3 = 24U, + KHR_DF_SHIFT_BYTESPLANE4 = 0U, + KHR_DF_SHIFT_BYTESPLANE5 = 8U, + KHR_DF_SHIFT_BYTESPLANE6 = 16U, + KHR_DF_SHIFT_BYTESPLANE7 = 24U +} khr_df_shift_e; + +typedef enum _khr_df_mask_e { + KHR_DF_MASK_VENDORID = 0x1FFFFU, + KHR_DF_MASK_DESCRIPTORTYPE = 0x7FFFU, + KHR_DF_MASK_VERSIONNUMBER = 0xFFFFU, + KHR_DF_MASK_DESCRIPTORBLOCKSIZE = 0xFFFFU, + KHR_DF_MASK_MODEL = 0xFFU, + KHR_DF_MASK_PRIMARIES = 0xFFU, + KHR_DF_MASK_TRANSFER = 0xFFU, + KHR_DF_MASK_FLAGS = 0xFFU, + KHR_DF_MASK_TEXELBLOCKDIMENSION0 = 0xFFU, + KHR_DF_MASK_TEXELBLOCKDIMENSION1 = 0xFFU, + KHR_DF_MASK_TEXELBLOCKDIMENSION2 = 0xFFU, + KHR_DF_MASK_TEXELBLOCKDIMENSION3 = 0xFFU, + KHR_DF_MASK_BYTESPLANE0 = 0xFFU, + KHR_DF_MASK_BYTESPLANE1 = 0xFFU, + KHR_DF_MASK_BYTESPLANE2 = 0xFFU, + KHR_DF_MASK_BYTESPLANE3 = 0xFFU, + KHR_DF_MASK_BYTESPLANE4 = 0xFFU, + KHR_DF_MASK_BYTESPLANE5 = 0xFFU, + KHR_DF_MASK_BYTESPLANE6 = 0xFFU, + KHR_DF_MASK_BYTESPLANE7 = 0xFFU +} khr_df_mask_e; + +/* Helper macro: + Extract field X from basic descriptor block BDB */ +#define KHR_DFDVAL(BDB, X) \ + (((BDB)[KHR_DF_WORD_ ## X] >> (KHR_DF_SHIFT_ ## X)) \ + & (KHR_DF_MASK_ ## X)) + +/* Helper macro: + Set field X of basic descriptor block BDB */ +#define KHR_DFDSETVAL(BDB, X, val) \ + ((BDB)[KHR_DF_WORD_ ## X] = \ + ((BDB)[KHR_DF_WORD_ ## X] & \ + ~((KHR_DF_MASK_ ## X) << (KHR_DF_SHIFT_ ## X))) | \ + (((val) & (KHR_DF_MASK_ ## X)) << (KHR_DF_SHIFT_ ## X))) + +/* Offsets relative to the start of a sample */ +typedef enum _khr_df_sampleword_e { + KHR_DF_SAMPLEWORD_BITOFFSET = 0U, + KHR_DF_SAMPLEWORD_BITLENGTH = 0U, + KHR_DF_SAMPLEWORD_CHANNELID = 0U, + KHR_DF_SAMPLEWORD_QUALIFIERS = 0U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION0 = 1U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION1 = 1U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION2 = 1U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION3 = 1U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL = 1U, + KHR_DF_SAMPLEWORD_SAMPLELOWER = 2U, + KHR_DF_SAMPLEWORD_SAMPLEUPPER = 3U +} khr_df_sampleword_e; + +typedef enum _khr_df_sampleshift_e { + KHR_DF_SAMPLESHIFT_BITOFFSET = 0U, + KHR_DF_SAMPLESHIFT_BITLENGTH = 16U, + KHR_DF_SAMPLESHIFT_CHANNELID = 24U, + /* N.B. Qualifiers are defined as an offset into a byte */ + KHR_DF_SAMPLESHIFT_QUALIFIERS = 24U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION0 = 0U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION1 = 8U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION2 = 16U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION3 = 24U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION_ALL = 0U, + KHR_DF_SAMPLESHIFT_SAMPLELOWER = 0U, + KHR_DF_SAMPLESHIFT_SAMPLEUPPER = 0U +} khr_df_sampleshift_e; + +typedef enum _khr_df_samplemask_e { + KHR_DF_SAMPLEMASK_BITOFFSET = 0xFFFFU, + KHR_DF_SAMPLEMASK_BITLENGTH = 0xFF, + KHR_DF_SAMPLEMASK_CHANNELID = 0xF, + /* N.B. Qualifiers are defined as an offset into a byte */ + KHR_DF_SAMPLEMASK_QUALIFIERS = 0xF0, + KHR_DF_SAMPLEMASK_SAMPLEPOSITION0 = 0xFF, + KHR_DF_SAMPLEMASK_SAMPLEPOSITION1 = 0xFF, + KHR_DF_SAMPLEMASK_SAMPLEPOSITION2 = 0xFF, + KHR_DF_SAMPLEMASK_SAMPLEPOSITION3 = 0xFF, + /* ISO C restricts enum values to range of int hence the + cast. We do it verbosely instead of using -1 to ensure + it is a 32-bit value even if int is 64 bits. */ + KHR_DF_SAMPLEMASK_SAMPLEPOSITION_ALL = (int) 0xFFFFFFFFU, + KHR_DF_SAMPLEMASK_SAMPLELOWER = (int) 0xFFFFFFFFU, + KHR_DF_SAMPLEMASK_SAMPLEUPPER = (int) 0xFFFFFFFFU +} khr_df_samplemask_e; + +/* Helper macro: + Extract field X of sample S from basic descriptor block BDB */ +#define KHR_DFDSVAL(BDB, S, X) \ + (((BDB)[KHR_DF_WORD_SAMPLESTART + \ + ((S) * KHR_DF_WORD_SAMPLEWORDS) + \ + KHR_DF_SAMPLEWORD_ ## X] >> (KHR_DF_SAMPLESHIFT_ ## X)) \ + & (KHR_DF_SAMPLEMASK_ ## X)) + +/* Helper macro: + Set field X of sample S of basic descriptor block BDB */ +#define KHR_DFDSETSVAL(BDB, S, X, val) \ + ((BDB)[KHR_DF_WORD_SAMPLESTART + \ + ((S) * KHR_DF_WORD_SAMPLEWORDS) + \ + KHR_DF_SAMPLEWORD_ ## X] = \ + ((BDB)[KHR_DF_WORD_SAMPLESTART + \ + ((S) * KHR_DF_WORD_SAMPLEWORDS) + \ + KHR_DF_SAMPLEWORD_ ## X] & \ + ~((uint32_t)(KHR_DF_SAMPLEMASK_ ## X) << (KHR_DF_SAMPLESHIFT_ ## X))) | \ + (((val) & (uint32_t)(KHR_DF_SAMPLEMASK_ ## X)) << (KHR_DF_SAMPLESHIFT_ ## X))) + +/* Helper macro: + Number of samples in basic descriptor block BDB */ +#define KHR_DFDSAMPLECOUNT(BDB) \ + (((KHR_DFDVAL(BDB, DESCRIPTORBLOCKSIZE) >> 2) - \ + KHR_DF_WORD_SAMPLESTART) \ + / KHR_DF_WORD_SAMPLEWORDS) + +/* Helper macro: + Size in words of basic descriptor block for S samples */ +#define KHR_DFDSIZEWORDS(S) \ + (KHR_DF_WORD_SAMPLESTART + \ + (S) * KHR_DF_WORD_SAMPLEWORDS) + +/* Vendor ids */ +typedef enum _khr_df_vendorid_e { + /* Standard Khronos descriptor */ + KHR_DF_VENDORID_KHRONOS = 0U, + KHR_DF_VENDORID_MAX = 0x1FFFFU +} khr_df_vendorid_e; + +/* Descriptor types */ +typedef enum _khr_df_khr_descriptortype_e { + /* Default Khronos basic descriptor block */ + KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT = 0U, + /* Extension descriptor block for additional planes */ + KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_PLANES = 0x6001U, + /* Extension descriptor block for additional dimensions */ + KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_DIMENSIONS = 0x6002U, + /* Bit indicates modifying requires understanding this extension */ + KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_WRITE_BIT = 0x2000U, + /* Bit indicates processing requires understanding this extension */ + KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_DECODE_BIT = 0x4000U, + KHR_DF_KHR_DESCRIPTORTYPE_MAX = 0x7FFFU +} khr_df_khr_descriptortype_e; + +/* Descriptor block version */ +typedef enum _khr_df_versionnumber_e { + /* Standard Khronos descriptor */ + KHR_DF_VERSIONNUMBER_1_0 = 0U, /* Version 1.0 of the specification */ + KHR_DF_VERSIONNUMBER_1_1 = 0U, /* Version 1.1 did not bump the version number */ + KHR_DF_VERSIONNUMBER_1_2 = 1U, /* Version 1.2 increased the version number */ + KHR_DF_VERSIONNUMBER_1_3 = 2U, /* Version 1.3 increased the version number */ + KHR_DF_VERSIONNUMBER_LATEST = KHR_DF_VERSIONNUMBER_1_3, + KHR_DF_VERSIONNUMBER_MAX = 0xFFFFU +} khr_df_versionnumber_e; + +/* Model in which the color coordinate space is defined. + There is no requirement that a color format use all the + channel types that are defined in the color model. */ +typedef enum _khr_df_model_e { + /* No interpretation of color channels defined */ + KHR_DF_MODEL_UNSPECIFIED = 0U, + /* Color primaries (red, green, blue) + alpha, depth and stencil */ + KHR_DF_MODEL_RGBSDA = 1U, + /* Color differences (Y', Cb, Cr) + alpha, depth and stencil */ + KHR_DF_MODEL_YUVSDA = 2U, + /* Color differences (Y', I, Q) + alpha, depth and stencil */ + KHR_DF_MODEL_YIQSDA = 3U, + /* Perceptual color (CIE L*a*b*) + alpha, depth and stencil */ + KHR_DF_MODEL_LABSDA = 4U, + /* Subtractive colors (cyan, magenta, yellow, black) + alpha */ + KHR_DF_MODEL_CMYKA = 5U, + /* Non-color coordinate data (X, Y, Z, W) */ + KHR_DF_MODEL_XYZW = 6U, + /* Hue, saturation, value, hue angle on color circle, plus alpha */ + KHR_DF_MODEL_HSVA_ANG = 7U, + /* Hue, saturation, lightness, hue angle on color circle, plus alpha */ + KHR_DF_MODEL_HSLA_ANG = 8U, + /* Hue, saturation, value, hue on color hexagon, plus alpha */ + KHR_DF_MODEL_HSVA_HEX = 9U, + /* Hue, saturation, lightness, hue on color hexagon, plus alpha */ + KHR_DF_MODEL_HSLA_HEX = 10U, + /* Lightweight approximate color difference (luma, orange, green) */ + KHR_DF_MODEL_YCGCOA = 11U, + /* ITU BT.2020 constant luminance YcCbcCrc */ + KHR_DF_MODEL_YCCBCCRC = 12U, + /* ITU BT.2100 constant intensity ICtCp */ + KHR_DF_MODEL_ICTCP = 13U, + /* CIE 1931 XYZ color coordinates (X, Y, Z) */ + KHR_DF_MODEL_CIEXYZ = 14U, + /* CIE 1931 xyY color coordinates (X, Y, Y) */ + KHR_DF_MODEL_CIEXYY = 15U, + + /* Compressed formats start at 128. */ + /* These compressed formats should generally have a single sample, + sited at the 0,0 position of the texel block. Where multiple + channels are used to distinguish formats, these should be cosited. */ + /* Direct3D (and S3) compressed formats */ + /* Note that premultiplied status is recorded separately */ + /* DXT1 "channels" are RGB (0), Alpha (1) */ + /* DXT1/BC1 with one channel is opaque */ + /* DXT1/BC1 with a cosited alpha sample is transparent */ + KHR_DF_MODEL_DXT1A = 128U, + KHR_DF_MODEL_BC1A = 128U, + /* DXT2/DXT3/BC2, with explicit 4-bit alpha */ + KHR_DF_MODEL_DXT2 = 129U, + KHR_DF_MODEL_DXT3 = 129U, + KHR_DF_MODEL_BC2 = 129U, + /* DXT4/DXT5/BC3, with interpolated alpha */ + KHR_DF_MODEL_DXT4 = 130U, + KHR_DF_MODEL_DXT5 = 130U, + KHR_DF_MODEL_BC3 = 130U, + /* BC4 - single channel interpolated 8-bit data */ + /* (The UNORM/SNORM variation is recorded in the channel data) */ + KHR_DF_MODEL_BC4 = 131U, + /* BC5 - two channel interpolated 8-bit data */ + /* (The UNORM/SNORM variation is recorded in the channel data) */ + KHR_DF_MODEL_BC5 = 132U, + /* BC6H - DX11 format for 16-bit float channels */ + KHR_DF_MODEL_BC6H = 133U, + /* BC7 - DX11 format */ + KHR_DF_MODEL_BC7 = 134U, + /* Gap left for future desktop expansion */ + + /* Mobile compressed formats follow */ + /* A format of ETC1 indicates that the format shall be decodable + by an ETC1-compliant decoder and not rely on ETC2 features */ + KHR_DF_MODEL_ETC1 = 160U, + /* A format of ETC2 is permitted to use ETC2 encodings on top of + the baseline ETC1 specification */ + /* The ETC2 format has channels "red", "green", "RGB" and "alpha", + which should be cosited samples */ + /* Punch-through alpha can be distinguished from full alpha by + the plane size in bytes required for the texel block */ + KHR_DF_MODEL_ETC2 = 161U, + /* Adaptive Scalable Texture Compression */ + /* ASTC HDR vs LDR is determined by the float flag in the channel */ + /* ASTC block size can be distinguished by texel block size */ + KHR_DF_MODEL_ASTC = 162U, + /* ETC1S is a simplified subset of ETC1 */ + KHR_DF_MODEL_ETC1S = 163U, + /* PowerVR Texture Compression */ + KHR_DF_MODEL_PVRTC = 164U, + KHR_DF_MODEL_PVRTC2 = 165U, + KHR_DF_MODEL_UASTC = 166U, + /* Proprietary formats (ATITC, etc.) should follow */ + KHR_DF_MODEL_MAX = 0xFFU +} khr_df_model_e; + +/* Definition of channel names for each color model */ +typedef enum _khr_df_model_channels_e { + /* Unspecified format with nominal channel numbering */ + KHR_DF_CHANNEL_UNSPECIFIED_0 = 0U, + KHR_DF_CHANNEL_UNSPECIFIED_1 = 1U, + KHR_DF_CHANNEL_UNSPECIFIED_2 = 2U, + KHR_DF_CHANNEL_UNSPECIFIED_3 = 3U, + KHR_DF_CHANNEL_UNSPECIFIED_4 = 4U, + KHR_DF_CHANNEL_UNSPECIFIED_5 = 5U, + KHR_DF_CHANNEL_UNSPECIFIED_6 = 6U, + KHR_DF_CHANNEL_UNSPECIFIED_7 = 7U, + KHR_DF_CHANNEL_UNSPECIFIED_8 = 8U, + KHR_DF_CHANNEL_UNSPECIFIED_9 = 9U, + KHR_DF_CHANNEL_UNSPECIFIED_10 = 10U, + KHR_DF_CHANNEL_UNSPECIFIED_11 = 11U, + KHR_DF_CHANNEL_UNSPECIFIED_12 = 12U, + KHR_DF_CHANNEL_UNSPECIFIED_13 = 13U, + KHR_DF_CHANNEL_UNSPECIFIED_14 = 14U, + KHR_DF_CHANNEL_UNSPECIFIED_15 = 15U, + /* MODEL_RGBSDA - red, green, blue, stencil, depth, alpha */ + KHR_DF_CHANNEL_RGBSDA_RED = 0U, + KHR_DF_CHANNEL_RGBSDA_R = 0U, + KHR_DF_CHANNEL_RGBSDA_GREEN = 1U, + KHR_DF_CHANNEL_RGBSDA_G = 1U, + KHR_DF_CHANNEL_RGBSDA_BLUE = 2U, + KHR_DF_CHANNEL_RGBSDA_B = 2U, + KHR_DF_CHANNEL_RGBSDA_STENCIL = 13U, + KHR_DF_CHANNEL_RGBSDA_S = 13U, + KHR_DF_CHANNEL_RGBSDA_DEPTH = 14U, + KHR_DF_CHANNEL_RGBSDA_D = 14U, + KHR_DF_CHANNEL_RGBSDA_ALPHA = 15U, + KHR_DF_CHANNEL_RGBSDA_A = 15U, + /* MODEL_YUVSDA - luma, Cb, Cr, stencil, depth, alpha */ + KHR_DF_CHANNEL_YUVSDA_Y = 0U, + KHR_DF_CHANNEL_YUVSDA_CB = 1U, + KHR_DF_CHANNEL_YUVSDA_U = 1U, + KHR_DF_CHANNEL_YUVSDA_CR = 2U, + KHR_DF_CHANNEL_YUVSDA_V = 2U, + KHR_DF_CHANNEL_YUVSDA_STENCIL = 13U, + KHR_DF_CHANNEL_YUVSDA_S = 13U, + KHR_DF_CHANNEL_YUVSDA_DEPTH = 14U, + KHR_DF_CHANNEL_YUVSDA_D = 14U, + KHR_DF_CHANNEL_YUVSDA_ALPHA = 15U, + KHR_DF_CHANNEL_YUVSDA_A = 15U, + /* MODEL_YIQSDA - luma, in-phase, quadrature, stencil, depth, alpha */ + KHR_DF_CHANNEL_YIQSDA_Y = 0U, + KHR_DF_CHANNEL_YIQSDA_I = 1U, + KHR_DF_CHANNEL_YIQSDA_Q = 2U, + KHR_DF_CHANNEL_YIQSDA_STENCIL = 13U, + KHR_DF_CHANNEL_YIQSDA_S = 13U, + KHR_DF_CHANNEL_YIQSDA_DEPTH = 14U, + KHR_DF_CHANNEL_YIQSDA_D = 14U, + KHR_DF_CHANNEL_YIQSDA_ALPHA = 15U, + KHR_DF_CHANNEL_YIQSDA_A = 15U, + /* MODEL_LABSDA - CIELAB/L*a*b* luma, red-green, blue-yellow, stencil, depth, alpha */ + KHR_DF_CHANNEL_LABSDA_L = 0U, + KHR_DF_CHANNEL_LABSDA_A = 1U, + KHR_DF_CHANNEL_LABSDA_B = 2U, + KHR_DF_CHANNEL_LABSDA_STENCIL = 13U, + KHR_DF_CHANNEL_LABSDA_S = 13U, + KHR_DF_CHANNEL_LABSDA_DEPTH = 14U, + KHR_DF_CHANNEL_LABSDA_D = 14U, + KHR_DF_CHANNEL_LABSDA_ALPHA = 15U, + /* NOTE: KHR_DF_CHANNEL_LABSDA_A is not a synonym for alpha! */ + /* MODEL_CMYKA - cyan, magenta, yellow, key/blacK, alpha */ + KHR_DF_CHANNEL_CMYKSDA_CYAN = 0U, + KHR_DF_CHANNEL_CMYKSDA_C = 0U, + KHR_DF_CHANNEL_CMYKSDA_MAGENTA = 1U, + KHR_DF_CHANNEL_CMYKSDA_M = 1U, + KHR_DF_CHANNEL_CMYKSDA_YELLOW = 2U, + KHR_DF_CHANNEL_CMYKSDA_Y = 2U, + KHR_DF_CHANNEL_CMYKSDA_KEY = 3U, + KHR_DF_CHANNEL_CMYKSDA_BLACK = 3U, + KHR_DF_CHANNEL_CMYKSDA_K = 3U, + KHR_DF_CHANNEL_CMYKSDA_ALPHA = 15U, + KHR_DF_CHANNEL_CMYKSDA_A = 15U, + /* MODEL_XYZW - coordinates x, y, z, w */ + KHR_DF_CHANNEL_XYZW_X = 0U, + KHR_DF_CHANNEL_XYZW_Y = 1U, + KHR_DF_CHANNEL_XYZW_Z = 2U, + KHR_DF_CHANNEL_XYZW_W = 3U, + /* MODEL_HSVA_ANG - value (luma), saturation, hue, alpha, angular projection, conical space */ + KHR_DF_CHANNEL_HSVA_ANG_VALUE = 0U, + KHR_DF_CHANNEL_HSVA_ANG_V = 0U, + KHR_DF_CHANNEL_HSVA_ANG_SATURATION = 1U, + KHR_DF_CHANNEL_HSVA_ANG_S = 1U, + KHR_DF_CHANNEL_HSVA_ANG_HUE = 2U, + KHR_DF_CHANNEL_HSVA_ANG_H = 2U, + KHR_DF_CHANNEL_HSVA_ANG_ALPHA = 15U, + KHR_DF_CHANNEL_HSVA_ANG_A = 15U, + /* MODEL_HSLA_ANG - lightness (luma), saturation, hue, alpha, angular projection, double conical space */ + KHR_DF_CHANNEL_HSLA_ANG_LIGHTNESS = 0U, + KHR_DF_CHANNEL_HSLA_ANG_L = 0U, + KHR_DF_CHANNEL_HSLA_ANG_SATURATION = 1U, + KHR_DF_CHANNEL_HSLA_ANG_S = 1U, + KHR_DF_CHANNEL_HSLA_ANG_HUE = 2U, + KHR_DF_CHANNEL_HSLA_ANG_H = 2U, + KHR_DF_CHANNEL_HSLA_ANG_ALPHA = 15U, + KHR_DF_CHANNEL_HSLA_ANG_A = 15U, + /* MODEL_HSVA_HEX - value (luma), saturation, hue, alpha, hexagonal projection, conical space */ + KHR_DF_CHANNEL_HSVA_HEX_VALUE = 0U, + KHR_DF_CHANNEL_HSVA_HEX_V = 0U, + KHR_DF_CHANNEL_HSVA_HEX_SATURATION = 1U, + KHR_DF_CHANNEL_HSVA_HEX_S = 1U, + KHR_DF_CHANNEL_HSVA_HEX_HUE = 2U, + KHR_DF_CHANNEL_HSVA_HEX_H = 2U, + KHR_DF_CHANNEL_HSVA_HEX_ALPHA = 15U, + KHR_DF_CHANNEL_HSVA_HEX_A = 15U, + /* MODEL_HSLA_HEX - lightness (luma), saturation, hue, alpha, hexagonal projection, double conical space */ + KHR_DF_CHANNEL_HSLA_HEX_LIGHTNESS = 0U, + KHR_DF_CHANNEL_HSLA_HEX_L = 0U, + KHR_DF_CHANNEL_HSLA_HEX_SATURATION = 1U, + KHR_DF_CHANNEL_HSLA_HEX_S = 1U, + KHR_DF_CHANNEL_HSLA_HEX_HUE = 2U, + KHR_DF_CHANNEL_HSLA_HEX_H = 2U, + KHR_DF_CHANNEL_HSLA_HEX_ALPHA = 15U, + KHR_DF_CHANNEL_HSLA_HEX_A = 15U, + /* MODEL_YCGCOA - luma, green delta, orange delta, alpha */ + KHR_DF_CHANNEL_YCGCOA_Y = 0U, + KHR_DF_CHANNEL_YCGCOA_CG = 1U, + KHR_DF_CHANNEL_YCGCOA_CO = 2U, + KHR_DF_CHANNEL_YCGCOA_ALPHA = 15U, + KHR_DF_CHANNEL_YCGCOA_A = 15U, + /* MODEL_CIEXYZ - CIE 1931 X, Y, Z */ + KHR_DF_CHANNEL_CIEXYZ_X = 0U, + KHR_DF_CHANNEL_CIEXYZ_Y = 1U, + KHR_DF_CHANNEL_CIEXYZ_Z = 2U, + /* MODEL_CIEXYY - CIE 1931 x, y, Y */ + KHR_DF_CHANNEL_CIEXYY_X = 0U, + KHR_DF_CHANNEL_CIEXYY_YCHROMA = 1U, + KHR_DF_CHANNEL_CIEXYY_YLUMA = 2U, + + /* Compressed formats */ + /* MODEL_DXT1A/MODEL_BC1A */ + KHR_DF_CHANNEL_DXT1A_COLOR = 0U, + KHR_DF_CHANNEL_BC1A_COLOR = 0U, + KHR_DF_CHANNEL_DXT1A_ALPHAPRESENT = 1U, + KHR_DF_CHANNEL_DXT1A_ALPHA = 1U, + KHR_DF_CHANNEL_BC1A_ALPHAPRESENT = 1U, + KHR_DF_CHANNEL_BC1A_ALPHA = 1U, + /* MODEL_DXT2/3/MODEL_BC2 */ + KHR_DF_CHANNEL_DXT2_COLOR = 0U, + KHR_DF_CHANNEL_DXT3_COLOR = 0U, + KHR_DF_CHANNEL_BC2_COLOR = 0U, + KHR_DF_CHANNEL_DXT2_ALPHA = 15U, + KHR_DF_CHANNEL_DXT3_ALPHA = 15U, + KHR_DF_CHANNEL_BC2_ALPHA = 15U, + /* MODEL_DXT4/5/MODEL_BC3 */ + KHR_DF_CHANNEL_DXT4_COLOR = 0U, + KHR_DF_CHANNEL_DXT5_COLOR = 0U, + KHR_DF_CHANNEL_BC3_COLOR = 0U, + KHR_DF_CHANNEL_DXT4_ALPHA = 15U, + KHR_DF_CHANNEL_DXT5_ALPHA = 15U, + KHR_DF_CHANNEL_BC3_ALPHA = 15U, + /* MODEL_BC4 */ + KHR_DF_CHANNEL_BC4_DATA = 0U, + /* MODEL_BC5 */ + KHR_DF_CHANNEL_BC5_RED = 0U, + KHR_DF_CHANNEL_BC5_R = 0U, + KHR_DF_CHANNEL_BC5_GREEN = 1U, + KHR_DF_CHANNEL_BC5_G = 1U, + /* MODEL_BC6H */ + KHR_DF_CHANNEL_BC6H_COLOR = 0U, + KHR_DF_CHANNEL_BC6H_DATA = 0U, + /* MODEL_BC7 */ + KHR_DF_CHANNEL_BC7_DATA = 0U, + KHR_DF_CHANNEL_BC7_COLOR = 0U, + /* MODEL_ETC1 */ + KHR_DF_CHANNEL_ETC1_DATA = 0U, + KHR_DF_CHANNEL_ETC1_COLOR = 0U, + /* MODEL_ETC2 */ + KHR_DF_CHANNEL_ETC2_RED = 0U, + KHR_DF_CHANNEL_ETC2_R = 0U, + KHR_DF_CHANNEL_ETC2_GREEN = 1U, + KHR_DF_CHANNEL_ETC2_G = 1U, + KHR_DF_CHANNEL_ETC2_COLOR = 2U, + KHR_DF_CHANNEL_ETC2_ALPHA = 15U, + KHR_DF_CHANNEL_ETC2_A = 15U, + /* MODEL_ASTC */ + KHR_DF_CHANNEL_ASTC_DATA = 0U, + /* MODEL_ETC1S */ + KHR_DF_CHANNEL_ETC1S_RGB = 0U, + KHR_DF_CHANNEL_ETC1S_RRR = 3U, + KHR_DF_CHANNEL_ETC1S_GGG = 4U, + KHR_DF_CHANNEL_ETC1S_AAA = 15U, + /* MODEL_PVRTC */ + KHR_DF_CHANNEL_PVRTC_DATA = 0U, + KHR_DF_CHANNEL_PVRTC_COLOR = 0U, + /* MODEL_PVRTC2 */ + KHR_DF_CHANNEL_PVRTC2_DATA = 0U, + KHR_DF_CHANNEL_PVRTC2_COLOR = 0U, + /* MODEL UASTC */ + KHR_DF_CHANNEL_UASTC_DATA = 0U, + KHR_DF_CHANNEL_UASTC_RGB = 0U, + KHR_DF_CHANNEL_UASTC_RGBA = 3U, + KHR_DF_CHANNEL_UASTC_RRR = 4U, + KHR_DF_CHANNEL_UASTC_RRRG = 5U, + KHR_DF_CHANNEL_UASTC_RG = 6U, + + /* Common channel names shared by multiple formats */ + KHR_DF_CHANNEL_COMMON_LUMA = 0U, + KHR_DF_CHANNEL_COMMON_L = 0U, + KHR_DF_CHANNEL_COMMON_STENCIL = 13U, + KHR_DF_CHANNEL_COMMON_S = 13U, + KHR_DF_CHANNEL_COMMON_DEPTH = 14U, + KHR_DF_CHANNEL_COMMON_D = 14U, + KHR_DF_CHANNEL_COMMON_ALPHA = 15U, + KHR_DF_CHANNEL_COMMON_A = 15U +} khr_df_model_channels_e; + +/* Definition of the primary colors in color coordinates. + This is implicitly responsible for defining the conversion + between RGB an YUV color spaces. + LAB and related absolute color models should use + KHR_DF_PRIMARIES_CIEXYZ. */ +typedef enum _khr_df_primaries_e { + /* No color primaries defined */ + KHR_DF_PRIMARIES_UNSPECIFIED = 0U, + /* Color primaries of ITU-R BT.709 and sRGB */ + KHR_DF_PRIMARIES_BT709 = 1U, + /* Synonym for KHR_DF_PRIMARIES_BT709 */ + KHR_DF_PRIMARIES_SRGB = 1U, + /* Color primaries of ITU-R BT.601 (625-line EBU variant) */ + KHR_DF_PRIMARIES_BT601_EBU = 2U, + /* Color primaries of ITU-R BT.601 (525-line SMPTE C variant) */ + KHR_DF_PRIMARIES_BT601_SMPTE = 3U, + /* Color primaries of ITU-R BT.2020 */ + KHR_DF_PRIMARIES_BT2020 = 4U, + /* CIE theoretical color coordinate space */ + KHR_DF_PRIMARIES_CIEXYZ = 5U, + /* Academy Color Encoding System primaries */ + KHR_DF_PRIMARIES_ACES = 6U, + /* Color primaries of ACEScc */ + KHR_DF_PRIMARIES_ACESCC = 7U, + /* Legacy NTSC 1953 primaries */ + KHR_DF_PRIMARIES_NTSC1953 = 8U, + /* Legacy PAL 525-line primaries */ + KHR_DF_PRIMARIES_PAL525 = 9U, + /* Color primaries of Display P3 */ + KHR_DF_PRIMARIES_DISPLAYP3 = 10U, + /* Color primaries of Adobe RGB (1998) */ + KHR_DF_PRIMARIES_ADOBERGB = 11U, + KHR_DF_PRIMARIES_MAX = 0xFFU +} khr_df_primaries_e; + +/* Definition of the optical to digital transfer function + ("gamma correction"). Most transfer functions are not a pure + power function and also include a linear element. + LAB and related absolute color representations should use + KHR_DF_TRANSFER_UNSPECIFIED. */ +typedef enum _khr_df_transfer_e { + /* No transfer function defined */ + KHR_DF_TRANSFER_UNSPECIFIED = 0U, + /* Linear transfer function (value proportional to intensity) */ + KHR_DF_TRANSFER_LINEAR = 1U, + /* Perceptually-linear transfer function of sRGH (~2.4) */ + KHR_DF_TRANSFER_SRGB = 2U, + /* Perceptually-linear transfer function of ITU BT.601, BT.709 and BT.2020 (~1/.45) */ + KHR_DF_TRANSFER_ITU = 3U, + /* SMTPE170M (digital NTSC) defines an alias for the ITU transfer function (~1/.45) */ + KHR_DF_TRANSFER_SMTPE170M = 3U, + /* Perceptually-linear gamma function of original NTSC (simple 2.2 gamma) */ + KHR_DF_TRANSFER_NTSC = 4U, + /* Sony S-log used by Sony video cameras */ + KHR_DF_TRANSFER_SLOG = 5U, + /* Sony S-log 2 used by Sony video cameras */ + KHR_DF_TRANSFER_SLOG2 = 6U, + /* ITU BT.1886 EOTF */ + KHR_DF_TRANSFER_BT1886 = 7U, + /* ITU BT.2100 HLG OETF */ + KHR_DF_TRANSFER_HLG_OETF = 8U, + /* ITU BT.2100 HLG EOTF */ + KHR_DF_TRANSFER_HLG_EOTF = 9U, + /* ITU BT.2100 PQ EOTF */ + KHR_DF_TRANSFER_PQ_EOTF = 10U, + /* ITU BT.2100 PQ OETF */ + KHR_DF_TRANSFER_PQ_OETF = 11U, + /* DCI P3 transfer function */ + KHR_DF_TRANSFER_DCIP3 = 12U, + /* Legacy PAL OETF */ + KHR_DF_TRANSFER_PAL_OETF = 13U, + /* Legacy PAL 625-line EOTF */ + KHR_DF_TRANSFER_PAL625_EOTF = 14U, + /* Legacy ST240 transfer function */ + KHR_DF_TRANSFER_ST240 = 15U, + /* ACEScc transfer function */ + KHR_DF_TRANSFER_ACESCC = 16U, + /* ACEScct transfer function */ + KHR_DF_TRANSFER_ACESCCT = 17U, + /* Adobe RGB (1998) transfer function */ + KHR_DF_TRANSFER_ADOBERGB = 18U, + KHR_DF_TRANSFER_MAX = 0xFFU +} khr_df_transfer_e; + +typedef enum _khr_df_flags_e { + KHR_DF_FLAG_ALPHA_STRAIGHT = 0U, + KHR_DF_FLAG_ALPHA_PREMULTIPLIED = 1U +} khr_df_flags_e; + +typedef enum _khr_df_sample_datatype_qualifiers_e { + KHR_DF_SAMPLE_DATATYPE_LINEAR = 1U << 4U, + KHR_DF_SAMPLE_DATATYPE_EXPONENT = 1U << 5U, + KHR_DF_SAMPLE_DATATYPE_SIGNED = 1U << 6U, + KHR_DF_SAMPLE_DATATYPE_FLOAT = 1U << 7U +} khr_df_sample_datatype_qualifiers_e; + +#endif diff --git a/thirdparty/libktx/include/ktx.h b/thirdparty/libktx/include/ktx.h new file mode 100644 index 0000000000..0af87f2519 --- /dev/null +++ b/thirdparty/libktx/include/ktx.h @@ -0,0 +1,1810 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +#ifndef KTX_H_A55A6F00956F42F3A137C11929827FE1 +#define KTX_H_A55A6F00956F42F3A137C11929827FE1 + +/* + * Copyright 2010-2018 The Khronos Group, Inc. + * SPDX-License-Identifier: Apache-2.0 + * + * See the accompanying LICENSE.md for licensing details for all files in + * the KTX library and KTX loader tests. + */ + +/** + * @file + * @~English + * + * @brief Declares the public functions and structures of the + * KTX API. + * + * @author Mark Callow, Edgewise Consulting and while at HI Corporation + * @author Based on original work by Georg Kolling, Imagination Technology + * + * @snippet{doc} version.h API version + */ + +#include <stdio.h> +#include <stdbool.h> +#include <sys/types.h> + +#include <KHR/khr_df.h> + +/* + * Don't use khrplatform.h in order not to break apps existing + * before these definitions were needed. + */ +#if defined(KHRONOS_STATIC) + #define KTX_API +#elif defined(_WIN32) || defined(__CYGWIN__) + #if !defined(KTX_API) + #if __GNUC__ + #define KTX_API __attribute__ ((dllimport)) + #elif _MSC_VER + #define KTX_API __declspec(dllimport) + #else + #error "Your compiler's equivalent of dllimport is unknown" + #endif + #endif +#elif defined(__ANDROID__) + #define KTX_API __attribute__((visibility("default"))) +#else + #define KTX_API +#endif + +#if defined(_WIN32) && !defined(KHRONOS_STATIC) + #if !defined(KTX_APIENTRY) + #define KTX_APIENTRY __stdcall + #endif +#else + #define KTX_APIENTRY +#endif + +/* To avoid including <KHR/khrplatform.h> define our own types. */ +typedef unsigned char ktx_uint8_t; +typedef bool ktx_bool_t; +#ifdef _MSC_VER +typedef unsigned __int16 ktx_uint16_t; +typedef signed __int16 ktx_int16_t; +typedef unsigned __int32 ktx_uint32_t; +typedef signed __int32 ktx_int32_t; +typedef size_t ktx_size_t; +typedef unsigned __int64 ktx_uint64_t; +typedef signed __int64 ktx_int64_t; +#else +#include <stdint.h> +typedef uint16_t ktx_uint16_t; +typedef int16_t ktx_int16_t; +typedef uint32_t ktx_uint32_t; +typedef int32_t ktx_int32_t; +typedef size_t ktx_size_t; +typedef uint64_t ktx_uint64_t; +typedef int64_t ktx_int64_t; +#endif + +/* This will cause compilation to fail if size of uint32 != 4. */ +typedef unsigned char ktx_uint32_t_SIZE_ASSERT[sizeof(ktx_uint32_t) == 4]; + +/* + * This #if allows libktx to be compiled with strict c99. It avoids + * compiler warnings or even errors when a gl.h is already included. + * "Redefinition of (type) is a c11 feature". Obviously this doesn't help if + * gl.h comes after. However nobody has complained about the unguarded typedefs + * since they were introduced so this is unlikely to be a problem in practice. + * Presumably everybody is using platform default compilers not c99 or else + * they are using C++. + */ +#if !defined(GL_NO_ERROR) + /* + * To avoid having to including gl.h ... + */ + typedef unsigned char GLboolean; + typedef unsigned int GLenum; + typedef int GLint; + typedef int GLsizei; + typedef unsigned int GLuint; + typedef unsigned char GLubyte; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @~English + * @brief Key string for standard writer metadata. + */ +#define KTX_ANIMDATA_KEY "KTXanimData" +/** + * @~English + * @brief Key string for standard orientation metadata. + */ +#define KTX_ORIENTATION_KEY "KTXorientation" +/** + * @~English + * @brief Key string for standard swizzle metadata. + */ +#define KTX_SWIZZLE_KEY "KTXswizzle" +/** + * @~English + * @brief Key string for standard writer metadata. + */ +#define KTX_WRITER_KEY "KTXwriter" +/** + * @~English + * @brief Key string for standard writer supercompression parameter metadata. + */ +#define KTX_WRITER_SCPARAMS_KEY "KTXwriterScParams" +/** + * @~English + * @brief Standard KTX 1 format for 1D orientation value. + */ +#define KTX_ORIENTATION1_FMT "S=%c" +/** + * @~English + * @brief Standard KTX 1 format for 2D orientation value. + */ +#define KTX_ORIENTATION2_FMT "S=%c,T=%c" +/** + * @~English + * @brief Standard KTX 1 format for 3D orientation value. + */ +#define KTX_ORIENTATION3_FMT "S=%c,T=%c,R=%c" +/** + * @~English + * @brief Required unpack alignment + */ +#define KTX_GL_UNPACK_ALIGNMENT 4 + +#define KTX_TRUE true +#define KTX_FALSE false + +/** + * @~English + * @brief Error codes returned by library functions. + */ +typedef enum ktx_error_code_e { + KTX_SUCCESS = 0, /*!< Operation was successful. */ + KTX_FILE_DATA_ERROR, /*!< The data in the file is inconsistent with the spec. */ + KTX_FILE_ISPIPE, /*!< The file is a pipe or named pipe. */ + KTX_FILE_OPEN_FAILED, /*!< The target file could not be opened. */ + KTX_FILE_OVERFLOW, /*!< The operation would exceed the max file size. */ + KTX_FILE_READ_ERROR, /*!< An error occurred while reading from the file. */ + KTX_FILE_SEEK_ERROR, /*!< An error occurred while seeking in the file. */ + KTX_FILE_UNEXPECTED_EOF, /*!< File does not have enough data to satisfy request. */ + KTX_FILE_WRITE_ERROR, /*!< An error occurred while writing to the file. */ + KTX_GL_ERROR, /*!< GL operations resulted in an error. */ + KTX_INVALID_OPERATION, /*!< The operation is not allowed in the current state. */ + KTX_INVALID_VALUE, /*!< A parameter value was not valid */ + KTX_NOT_FOUND, /*!< Requested key was not found */ + KTX_OUT_OF_MEMORY, /*!< Not enough memory to complete the operation. */ + KTX_TRANSCODE_FAILED, /*!< Transcoding of block compressed texture failed. */ + KTX_UNKNOWN_FILE_FORMAT, /*!< The file not a KTX file */ + KTX_UNSUPPORTED_TEXTURE_TYPE, /*!< The KTX file specifies an unsupported texture type. */ + KTX_UNSUPPORTED_FEATURE, /*!< Feature not included in in-use library or not yet implemented. */ + KTX_LIBRARY_NOT_LINKED, /*!< Library dependency (OpenGL or Vulkan) not linked into application. */ + KTX_ERROR_MAX_ENUM = KTX_LIBRARY_NOT_LINKED /*!< For safety checks. */ +} ktx_error_code_e; +/** + * @deprecated + * @~English + * @brief For backward compatibility + */ +#define KTX_error_code ktx_error_code_e + +#define KTX_IDENTIFIER_REF { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A } +#define KTX_ENDIAN_REF (0x04030201) +#define KTX_ENDIAN_REF_REV (0x01020304) +#define KTX_HEADER_SIZE (64) + +/** + * @~English + * @brief Result codes returned by library functions. + */ + typedef enum ktx_error_code_e ktxResult; + +/** + * @class ktxHashList + * @~English + * @brief Opaque handle to a ktxHashList. + */ +typedef struct ktxKVListEntry* ktxHashList; + +typedef struct ktxStream ktxStream; + +#define KTX_APIENTRYP KTX_APIENTRY * +/** + * @class ktxHashListEntry + * @~English + * @brief Opaque handle to an entry in a @ref ktxHashList. + */ +typedef struct ktxKVListEntry ktxHashListEntry; + +typedef enum ktxOrientationX { + KTX_ORIENT_X_LEFT = 'l', KTX_ORIENT_X_RIGHT = 'r' +} ktxOrientationX; + +typedef enum ktxOrientationY { + KTX_ORIENT_Y_UP = 'u', KTX_ORIENT_Y_DOWN = 'd' +} ktxOrientationY; + +typedef enum ktxOrientationZ { + KTX_ORIENT_Z_IN = 'i', KTX_ORIENT_Z_OUT = 'o' +} ktxOrientationZ; + +typedef enum class_id { + ktxTexture1_c = 1, + ktxTexture2_c = 2 +} class_id; + +/** + * @~English + * @brief Struct describing the logical orientation of an image. + */ +struct ktxOrientation { + ktxOrientationX x; /*!< Orientation in X */ + ktxOrientationY y; /*!< Orientation in Y */ + ktxOrientationZ z; /*!< Orientation in Z */ +}; + +#define KTXTEXTURECLASSDEFN \ + class_id classId; \ + struct ktxTexture_vtbl* vtbl; \ + struct ktxTexture_vvtbl* vvtbl; \ + struct ktxTexture_protected* _protected; \ + ktx_bool_t isArray; \ + ktx_bool_t isCubemap; \ + ktx_bool_t isCompressed; \ + ktx_bool_t generateMipmaps; \ + ktx_uint32_t baseWidth; \ + ktx_uint32_t baseHeight; \ + ktx_uint32_t baseDepth; \ + ktx_uint32_t numDimensions; \ + ktx_uint32_t numLevels; \ + ktx_uint32_t numLayers; \ + ktx_uint32_t numFaces; \ + struct ktxOrientation orientation; \ + ktxHashList kvDataHead; \ + ktx_uint32_t kvDataLen; \ + ktx_uint8_t* kvData; \ + ktx_size_t dataSize; \ + ktx_uint8_t* pData; + + +/** + * @class ktxTexture + * @~English + * @brief Base class representing a texture. + * + * ktxTextures should be created only by one of the provided + * functions and these fields should be considered read-only. + */ +typedef struct ktxTexture { + KTXTEXTURECLASSDEFN +} ktxTexture; +/** + * @typedef ktxTexture::classId + * @~English + * @brief Identify the class type. + * + * Since there are no public ktxTexture constructors, this can only have + * values of ktxTexture1_c or ktxTexture2_c. + */ +/** + * @typedef ktxTexture::vtbl + * @~English + * @brief Pointer to the class's vtble. + */ +/** + * @typedef ktxTexture::vvtbl + * @~English + * @brief Pointer to the class's vtble for Vulkan functions. + * + * A separate vtble is used so this header does not need to include vulkan.h. + */ +/** + * @typedef ktxTexture::_protected + * @~English + * @brief Opaque pointer to the class's protected variables. + */ +/** + * @typedef ktxTexture::isArray + * @~English + * + * KTX_TRUE if the texture is an array texture, i.e, + * a GL_TEXTURE_*_ARRAY target is to be used. + */ +/** + * @typedef ktxTexture::isCubemap + * @~English + * + * KTX_TRUE if the texture is a cubemap or cubemap array. + */ +/** + * @typedef ktxTexture::isCubemap + * @~English + * + * KTX_TRUE if the texture's format is a block compressed format. + */ +/** + * @typedef ktxTexture::generateMipmaps + * @~English + * + * KTX_TRUE if mipmaps should be generated for the texture by + * ktxTexture_GLUpload() or ktxTexture_VkUpload(). + */ +/**n + * @typedef ktxTexture::baseWidth + * @~English + * @brief Width of the texture's base level. + */ +/** + * @typedef ktxTexture::baseHeight + * @~English + * @brief Height of the texture's base level. + */ +/** + * @typedef ktxTexture::baseDepth + * @~English + * @brief Depth of the texture's base level. + */ +/** + * @typedef ktxTexture::numDimensions + * @~English + * @brief Number of dimensions in the texture: 1, 2 or 3. + */ +/** + * @typedef ktxTexture::numLevels + * @~English + * @brief Number of mip levels in the texture. + * + * Must be 1, if @c generateMipmaps is KTX_TRUE. Can be less than a + * full pyramid but always starts at the base level. + */ +/** + * @typedef ktxTexture::numLevels + * @~English + * @brief Number of array layers in the texture. + */ +/** + * @typedef ktxTexture::numFaces + * @~English + * @brief Number of faces: 6 for cube maps, 1 otherwise. + */ +/** + * @typedef ktxTexture::orientation + * @~English + * @brief Describes the logical orientation of the images in each dimension. + * + * ktxOrientationX for X, ktxOrientationY for Y and ktxOrientationZ for Z. + */ +/** + * @typedef ktxTexture::kvDataHead + * @~English + * @brief Head of the hash list of metadata. + */ +/** + * @typedef ktxTexture::kvDataLen + * @~English + * @brief Length of the metadata, if it has been extracted in its raw form, + * otherwise 0. + */ +/** + * @typedef ktxTexture::kvData + * @~English + * @brief Pointer to the metadata, if it has been extracted in its raw form, + * otherwise NULL. + */ +/** + * @typedef ktxTexture::dataSize + * @~English + * @brief Byte length of the texture's uncompressed image data. + */ +/** + * @typedef ktxTexture::pData + * @~English + * @brief Pointer to the start of the image data. + */ + +/** + * @memberof ktxTexture + * @~English + * @brief Signature of function called by the <tt>ktxTexture_Iterate*</tt> + * functions to receive image data. + * + * The function parameters are used to pass values which change for each image. + * Obtain values which are uniform across all images from the @c ktxTexture + * object. + * + * @param [in] miplevel MIP level from 0 to the max level which is + * dependent on the texture size. + * @param [in] face usually 0; for cube maps, one of the 6 cube + * faces in the order +X, -X, +Y, -Y, +Z, -Z, + * 0 to 5. + * @param [in] width width of the image. + * @param [in] height height of the image or, for 1D textures + * textures, 1. + * @param [in] depth depth of the image or, for 1D & 2D + * textures, 1. + * @param [in] faceLodSize number of bytes of data pointed at by + * @p pixels. + * @param [in] pixels pointer to the image data. + * @param [in,out] userdata pointer for the application to pass data to and + * from the callback function. + */ + +typedef KTX_error_code + (* PFNKTXITERCB)(int miplevel, int face, + int width, int height, int depth, + ktx_uint64_t faceLodSize, + void* pixels, void* userdata); + +/* Don't use KTX_APIENTRYP to avoid a Doxygen bug. */ +typedef void (KTX_APIENTRY* PFNKTEXDESTROY)(ktxTexture* This); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXGETIMAGEOFFSET)(ktxTexture* This, ktx_uint32_t level, + ktx_uint32_t layer, + ktx_uint32_t faceSlice, + ktx_size_t* pOffset); +typedef ktx_size_t + (KTX_APIENTRY* PFNKTEXGETDATASIZEUNCOMPRESSED)(ktxTexture* This); +typedef ktx_size_t + (KTX_APIENTRY* PFNKTEXGETIMAGESIZE)(ktxTexture* This, ktx_uint32_t level); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXITERATELEVELS)(ktxTexture* This, PFNKTXITERCB iterCb, + void* userdata); + +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXITERATELOADLEVELFACES)(ktxTexture* This, + PFNKTXITERCB iterCb, + void* userdata); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXLOADIMAGEDATA)(ktxTexture* This, + ktx_uint8_t* pBuffer, + ktx_size_t bufSize); +typedef ktx_bool_t + (KTX_APIENTRY* PFNKTEXNEEDSTRANSCODING)(ktxTexture* This); + +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXSETIMAGEFROMMEMORY)(ktxTexture* This, + ktx_uint32_t level, + ktx_uint32_t layer, + ktx_uint32_t faceSlice, + const ktx_uint8_t* src, + ktx_size_t srcSize); + +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXSETIMAGEFROMSTDIOSTREAM)(ktxTexture* This, + ktx_uint32_t level, + ktx_uint32_t layer, + ktx_uint32_t faceSlice, + FILE* src, ktx_size_t srcSize); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXWRITETOSTDIOSTREAM)(ktxTexture* This, FILE* dstsstr); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXWRITETONAMEDFILE)(ktxTexture* This, + const char* const dstname); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXWRITETOMEMORY)(ktxTexture* This, + ktx_uint8_t** bytes, ktx_size_t* size); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXWRITETOSTREAM)(ktxTexture* This, + ktxStream* dststr); + +/** + * @memberof ktxTexture + * @~English + * @brief Table of virtual ktxTexture methods. + */ + struct ktxTexture_vtbl { + PFNKTEXDESTROY Destroy; + PFNKTEXGETIMAGEOFFSET GetImageOffset; + PFNKTEXGETDATASIZEUNCOMPRESSED GetDataSizeUncompressed; + PFNKTEXGETIMAGESIZE GetImageSize; + PFNKTEXITERATELEVELS IterateLevels; + PFNKTEXITERATELOADLEVELFACES IterateLoadLevelFaces; + PFNKTEXNEEDSTRANSCODING NeedsTranscoding; + PFNKTEXLOADIMAGEDATA LoadImageData; + PFNKTEXSETIMAGEFROMMEMORY SetImageFromMemory; + PFNKTEXSETIMAGEFROMSTDIOSTREAM SetImageFromStdioStream; + PFNKTEXWRITETOSTDIOSTREAM WriteToStdioStream; + PFNKTEXWRITETONAMEDFILE WriteToNamedFile; + PFNKTEXWRITETOMEMORY WriteToMemory; + PFNKTEXWRITETOSTREAM WriteToStream; +}; + +/**************************************************************** + * Macros to give some backward compatibility to the previous API + ****************************************************************/ + +/** + * @~English + * @brief Helper for calling the Destroy virtual method of a ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_Destroy + */ +#define ktxTexture_Destroy(This) (This)->vtbl->Destroy(This) + +/** + * @~English + * @brief Helper for calling the GetImageOffset virtual method of a + * ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_GetImageOffset + */ +#define ktxTexture_GetImageOffset(This, level, layer, faceSlice, pOffset) \ + (This)->vtbl->GetImageOffset(This, level, layer, faceSlice, pOffset) + +/** + * @~English + * @brief Helper for calling the GetDataSizeUncompressed virtual method of a ktxTexture. + * + * For a ktxTexture1 this will always return the value of This->dataSize. + * + * @copydetails ktxTexture2.ktxTexture2_GetDataSizeUncompressed + */ +#define ktxTexture_GetDataSizeUncompressed(This) \ + (This)->vtbl->GetDataSizeUncompressed(This) + +/** + * @~English + * @brief Helper for calling the GetImageSize virtual method of a ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_GetImageSize + */ +#define ktxTexture_GetImageSize(This, level) \ + (This)->vtbl->GetImageSize(This, level) + +/** + * @~English + * @brief Helper for calling the IterateLevels virtual method of a ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_IterateLevels + */ +#define ktxTexture_IterateLevels(This, iterCb, userdata) \ + (This)->vtbl->IterateLevels(This, iterCb, userdata) + +/** + * @~English + * @brief Helper for calling the IterateLoadLevelFaces virtual method of a + * ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_IterateLoadLevelFaces + */ + #define ktxTexture_IterateLoadLevelFaces(This, iterCb, userdata) \ + (This)->vtbl->IterateLoadLevelFaces(This, iterCb, userdata) + +/** + * @~English + * @brief Helper for calling the LoadImageData virtual method of a ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_LoadImageData + */ +#define ktxTexture_LoadImageData(This, pBuffer, bufSize) \ + (This)->vtbl->LoadImageData(This, pBuffer, bufSize) + +/** + * @~English + * @brief Helper for calling the NeedsTranscoding virtual method of a ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_NeedsTranscoding + */ +#define ktxTexture_NeedsTranscoding(This) (This)->vtbl->NeedsTranscoding(This) + +/** + * @~English + * @brief Helper for calling the SetImageFromMemory virtual method of a + * ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_SetImageFromMemory + */ +#define ktxTexture_SetImageFromMemory(This, level, layer, faceSlice, \ + src, srcSize) \ + (This)->vtbl->SetImageFromMemory(This, level, layer, faceSlice, src, srcSize) + +/** + * @~English + * @brief Helper for calling the SetImageFromStdioStream virtual method of a + * ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_SetImageFromStdioStream + */ +#define ktxTexture_SetImageFromStdioStream(This, level, layer, faceSlice, \ + src, srcSize) \ + (This)->vtbl->SetImageFromStdioStream(This, level, layer, faceSlice, \ + src, srcSize) + +/** + * @~English + * @brief Helper for calling the WriteToStdioStream virtual method of a + * ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_WriteToStdioStream + */ +#define ktxTexture_WriteToStdioStream(This, dstsstr) \ + (This)->vtbl->WriteToStdioStream(This, dstsstr) + +/** + * @~English + * @brief Helper for calling the WriteToNamedfile virtual method of a + * ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_WriteToNamedFile + */ +#define ktxTexture_WriteToNamedFile(This, dstname) \ + (This)->vtbl->WriteToNamedFile(This, dstname) + +/** + * @~English + * @brief Helper for calling the WriteToMemory virtual method of a ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_WriteToMemory + */ +#define ktxTexture_WriteToMemory(This, ppDstBytes, pSize) \ + (This)->vtbl->WriteToMemory(This, ppDstBytes, pSize) + +/** + * @~English + * @brief Helper for calling the WriteToStream virtual method of a ktxTexture. + * @copydoc ktxTexture2.ktxTexture2_WriteToStream + */ +#define ktxTexture_WriteToStream(This, dststr) \ + (This)->vtbl->WriteToStream(This, dststr) + + +/** + * @class ktxTexture1 + * @~English + * @brief Class representing a KTX version 1 format texture. + * + * ktxTextures should be created only by one of the ktxTexture_Create* + * functions and these fields should be considered read-only. + */ +typedef struct ktxTexture1 { + KTXTEXTURECLASSDEFN + ktx_uint32_t glFormat; /*!< Format of the texture data, e.g., GL_RGB. */ + ktx_uint32_t glInternalformat; /*!< Internal format of the texture data, + e.g., GL_RGB8. */ + ktx_uint32_t glBaseInternalformat; /*!< Base format of the texture data, + e.g., GL_RGB. */ + ktx_uint32_t glType; /*!< Type of the texture data, e.g, GL_UNSIGNED_BYTE.*/ + struct ktxTexture1_private* _private; /*!< Private data. */ +} ktxTexture1; + +/*===========================================================* +* KTX format version 2 * +*===========================================================*/ + +/** + * @~English + * @brief Enumerators identifying the supercompression scheme. + */ +typedef enum ktxSupercmpScheme { + KTX_SS_NONE = 0, /*!< No supercompression. */ + KTX_SS_BASIS_LZ = 1, /*!< Basis LZ supercompression. */ + KTX_SS_ZSTD = 2, /*!< ZStd supercompression. */ + KTX_SS_BEGIN_RANGE = KTX_SS_NONE, + KTX_SS_END_RANGE = KTX_SS_ZSTD, + KTX_SS_BEGIN_VENDOR_RANGE = 0x10000, + KTX_SS_END_VENDOR_RANGE = 0x1ffff, + KTX_SS_BEGIN_RESERVED = 0x20000, + KTX_SUPERCOMPRESSION_BASIS = KTX_SS_BASIS_LZ, + /*!< @deprecated Will be removed before v4 release. Use KTX_SS_BASIS_LZ instead. */ + KTX_SUPERCOMPRESSION_ZSTD = KTX_SS_ZSTD + /*!< @deprecated Will be removed before v4 release. Use KTX_SS_ZSTD instead. */ +} ktxSupercmpScheme; + +/** + * @class ktxTexture2 + * @~English + * @brief Class representing a KTX version 2 format texture. + * + * ktxTextures should be created only by one of the ktxTexture_Create* + * functions and these fields should be considered read-only. + */ +typedef struct ktxTexture2 { + KTXTEXTURECLASSDEFN + ktx_uint32_t vkFormat; + ktx_uint32_t* pDfd; + ktxSupercmpScheme supercompressionScheme; + ktx_bool_t isVideo; + ktx_uint32_t duration; + ktx_uint32_t timescale; + ktx_uint32_t loopcount; + struct ktxTexture2_private* _private; /*!< Private data. */ +} ktxTexture2; + +#define ktxTexture(t) ((ktxTexture*)t) + +/** + * @memberof ktxTexture + * @~English + * @brief Structure for passing texture information to ktxTexture1_Create() and + * ktxTexture2_Create(). + * + * @sa ktxTexture1_Create() and ktxTexture2_Create(). + */ +typedef struct +{ + ktx_uint32_t glInternalformat; /*!< Internal format for the texture, e.g., + GL_RGB8. Ignored when creating a + ktxTexture2. */ + ktx_uint32_t vkFormat; /*!< VkFormat for texture. Ignored when creating a + ktxTexture1. */ + ktx_uint32_t* pDfd; /*!< Pointer to DFD. Used only when creating a + ktxTexture2 and only if vkFormat is + VK_FORMAT_UNDEFINED. */ + ktx_uint32_t baseWidth; /*!< Width of the base level of the texture. */ + ktx_uint32_t baseHeight; /*!< Height of the base level of the texture. */ + ktx_uint32_t baseDepth; /*!< Depth of the base level of the texture. */ + ktx_uint32_t numDimensions; /*!< Number of dimensions in the texture, 1, 2 + or 3. */ + ktx_uint32_t numLevels; /*!< Number of mip levels in the texture. Should be + 1 if @c generateMipmaps is KTX_TRUE; */ + ktx_uint32_t numLayers; /*!< Number of array layers in the texture. */ + ktx_uint32_t numFaces; /*!< Number of faces: 6 for cube maps, 1 otherwise. */ + ktx_bool_t isArray; /*!< Set to KTX_TRUE if the texture is to be an + array texture. Means OpenGL will use a + GL_TEXTURE_*_ARRAY target. */ + ktx_bool_t generateMipmaps; /*!< Set to KTX_TRUE if mipmaps should be + generated for the texture when loading + into a 3D API. */ +} ktxTextureCreateInfo; + +/** + * @memberof ktxTexture + * @~English + * @brief Enum for requesting, or not, allocation of storage for images. + * + * @sa ktxTexture1_Create() and ktxTexture2_Create(). + */ +typedef enum { + KTX_TEXTURE_CREATE_NO_STORAGE = 0, /*!< Don't allocate any image storage. */ + KTX_TEXTURE_CREATE_ALLOC_STORAGE = 1 /*!< Allocate image storage. */ +} ktxTextureCreateStorageEnum; + +/** + * @memberof ktxTexture + * @~English + * @brief Flags for requesting services during creation. + * + * @sa ktxTexture_CreateFrom* + */ +enum ktxTextureCreateFlagBits { + KTX_TEXTURE_CREATE_NO_FLAGS = 0x00, + KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT = 0x01, + /*!< Load the images from the KTX source. */ + KTX_TEXTURE_CREATE_RAW_KVDATA_BIT = 0x02, + /*!< Load the raw key-value data instead of + creating a @c ktxHashList from it. */ + KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT = 0x04 + /*!< Skip any key-value data. This overrides + the RAW_KVDATA_BIT. */ +}; +/** + * @memberof ktxTexture + * @~English + * @brief Type for TextureCreateFlags parameters. + * + * @sa ktxTexture_CreateFrom*() + */ +typedef ktx_uint32_t ktxTextureCreateFlags; + +/*===========================================================* +* ktxStream +*===========================================================*/ + +/* + * This is unsigned to allow ktxmemstreams to use the + * full amount of memory available. Platforms will + * limit the size of ktxfilestreams to, e.g, MAX_LONG + * on 32-bit and ktxfilestreams raises errors if + * offset values exceed the limits. This choice may + * need to be revisited if we ever start needing -ve + * offsets. + * + * Should the 2GB file size handling limit on 32-bit + * platforms become a problem, ktxfilestream will have + * to be changed to explicitly handle large files by + * using the 64-bit stream functions. + */ +#if defined(_MSC_VER) && defined(_WIN64) + typedef unsigned __int64 ktx_off_t; +#else + typedef off_t ktx_off_t; +#endif +typedef struct ktxMem ktxMem; +typedef struct ktxStream ktxStream; + +enum streamType { eStreamTypeFile = 1, eStreamTypeMemory = 2, eStreamTypeCustom = 3 }; + +/** + * @~English + * @brief type for a pointer to a stream reading function + */ +typedef KTX_error_code (*ktxStream_read)(ktxStream* str, void* dst, + const ktx_size_t count); +/** + * @~English + * @brief type for a pointer to a stream skipping function + */ +typedef KTX_error_code (*ktxStream_skip)(ktxStream* str, + const ktx_size_t count); + +/** + * @~English + * @brief type for a pointer to a stream writing function + */ +typedef KTX_error_code (*ktxStream_write)(ktxStream* str, const void *src, + const ktx_size_t size, + const ktx_size_t count); + +/** + * @~English + * @brief type for a pointer to a stream position query function + */ +typedef KTX_error_code (*ktxStream_getpos)(ktxStream* str, ktx_off_t* const offset); + +/** + * @~English + * @brief type for a pointer to a stream position query function + */ +typedef KTX_error_code (*ktxStream_setpos)(ktxStream* str, const ktx_off_t offset); + +/** + * @~English + * @brief type for a pointer to a stream size query function + */ +typedef KTX_error_code (*ktxStream_getsize)(ktxStream* str, ktx_size_t* const size); + +/** + * @~English + * @brief Destruct a stream + */ +typedef void (*ktxStream_destruct)(ktxStream* str); + +/** + * @~English + * + * @brief Interface of ktxStream. + * + * @author Maksim Kolesin + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ +struct ktxStream +{ + ktxStream_read read; /*!< pointer to function for reading bytes. */ + ktxStream_skip skip; /*!< pointer to function for skipping bytes. */ + ktxStream_write write; /*!< pointer to function for writing bytes. */ + ktxStream_getpos getpos; /*!< pointer to function for getting current position in stream. */ + ktxStream_setpos setpos; /*!< pointer to function for setting current position in stream. */ + ktxStream_getsize getsize; /*!< pointer to function for querying size. */ + ktxStream_destruct destruct; /*!< destruct the stream. */ + + enum streamType type; + union { + FILE* file; /**< a stdio FILE pointer for a ktxFileStream. */ + ktxMem* mem; /**< a pointer to a ktxMem struct for a ktxMemStream. */ + struct + { + void* address; /**< pointer to the data. */ + void* allocatorAddress; /**< pointer to a memory allocator. */ + ktx_size_t size; /**< size of the data. */ + } custom_ptr; /**< pointer to a struct for custom streams. */ + } data; /**< pointer to the stream data. */ + ktx_off_t readpos; /**< used by FileStream for stdin. */ + ktx_bool_t closeOnDestruct; /**< Close FILE* or dispose of memory on destruct. */ +}; + +/* + * See the implementation files for the full documentation of the following + * functions. + */ + +/* + * These four create a ktxTexture1 or ktxTexture2 according to the data + * header, and return a pointer to the base ktxTexture class. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_CreateFromStdioStream(FILE* stdioStream, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_CreateFromNamedFile(const char* const filename, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_CreateFromStream(ktxStream* stream, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex); + +/* + * Returns a pointer to the image data of a ktxTexture object. + */ +KTX_API ktx_uint8_t* KTX_APIENTRY +ktxTexture_GetData(ktxTexture* This); + +/* + * Returns the pitch of a row of an image at the specified level. + * Similar to the rowPitch in a VkSubResourceLayout. + */ +KTX_API ktx_uint32_t KTX_APIENTRY +ktxTexture_GetRowPitch(ktxTexture* This, ktx_uint32_t level); + + /* + * Return the element size of the texture's images. + */ +KTX_API ktx_uint32_t KTX_APIENTRY +ktxTexture_GetElementSize(ktxTexture* This); + +/* + * Returns the size of all the image data of a ktxTexture object in bytes. + */ +KTX_API ktx_size_t KTX_APIENTRY +ktxTexture_GetDataSize(ktxTexture* This); + +/* Uploads a texture to OpenGL {,ES}. */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_GLUpload(ktxTexture* This, GLuint* pTexture, GLenum* pTarget, + GLenum* pGlerror); + +/* + * Iterate over the levels or faces in a ktxTexture object. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_IterateLevelFaces(ktxTexture* This, PFNKTXITERCB iterCb, + void* userdata); +/* + * Create a new ktxTexture1. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_Create(ktxTextureCreateInfo* createInfo, + ktxTextureCreateStorageEnum storageAllocation, + ktxTexture1** newTex); + +/* + * These four create a ktxTexture1 provided the data is in KTX format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_CreateFromStdioStream(FILE* stdioStream, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_CreateFromNamedFile(const char* const filename, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_CreateFromStream(ktxStream* stream, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex); + +KTX_API ktx_bool_t KTX_APIENTRY +ktxTexture1_NeedsTranscoding(ktxTexture1* This); + +/* + * Write a ktxTexture object to a stdio stream in KTX format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_WriteKTX2ToStdioStream(ktxTexture1* This, FILE* dstsstr); + +/* + * Write a ktxTexture object to a named file in KTX format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_WriteKTX2ToNamedFile(ktxTexture1* This, const char* const dstname); + +/* + * Write a ktxTexture object to a block of memory in KTX format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_WriteKTX2ToMemory(ktxTexture1* This, + ktx_uint8_t** bytes, ktx_size_t* size); + +/* + * Write a ktxTexture object to a ktxStream in KTX format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_WriteKTX2ToStream(ktxTexture1* This, ktxStream *dststr); + +/* + * Create a new ktxTexture2. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_Create(ktxTextureCreateInfo* createInfo, + ktxTextureCreateStorageEnum storageAllocation, + ktxTexture2** newTex); + +/* + * Create a new ktxTexture2 as a copy of an existing texture. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CreateCopy(ktxTexture2* orig, ktxTexture2** newTex); + + /* + * These four create a ktxTexture2 provided the data is in KTX2 format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CreateFromStdioStream(FILE* stdioStream, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CreateFromNamedFile(const char* const filename, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CreateFromStream(ktxStream* stream, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CompressBasis(ktxTexture2* This, ktx_uint32_t quality); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_DeflateZstd(ktxTexture2* This, ktx_uint32_t level); + +KTX_API void KTX_APIENTRY +ktxTexture2_GetComponentInfo(ktxTexture2* This, ktx_uint32_t* numComponents, + ktx_uint32_t* componentByteLength); + +KTX_API ktx_uint32_t KTX_APIENTRY +ktxTexture2_GetNumComponents(ktxTexture2* This); + +KTX_API khr_df_transfer_e KTX_APIENTRY +ktxTexture2_GetOETF_e(ktxTexture2* This); + +// For backward compatibility +KTX_API ktx_uint32_t KTX_APIENTRY +ktxTexture2_GetOETF(ktxTexture2* This); + +KTX_API khr_df_model_e KTX_APIENTRY +ktxTexture2_GetColorModel_e(ktxTexture2* This); + +KTX_API ktx_bool_t KTX_APIENTRY +ktxTexture2_GetPremultipliedAlpha(ktxTexture2* This); + +KTX_API ktx_bool_t KTX_APIENTRY +ktxTexture2_NeedsTranscoding(ktxTexture2* This); + +/** + * @~English + * @brief Flags specifiying UASTC encoding options. + */ +typedef enum ktx_pack_uastc_flag_bits_e { + KTX_PACK_UASTC_LEVEL_FASTEST = 0, + /*!< Fastest compression. 43.45dB. */ + KTX_PACK_UASTC_LEVEL_FASTER = 1, + /*!< Faster compression. 46.49dB. */ + KTX_PACK_UASTC_LEVEL_DEFAULT = 2, + /*!< Default compression. 47.47dB. */ + KTX_PACK_UASTC_LEVEL_SLOWER = 3, + /*!< Slower compression. 48.01dB. */ + KTX_PACK_UASTC_LEVEL_VERYSLOW = 4, + /*!< Very slow compression. 48.24dB. */ + KTX_PACK_UASTC_MAX_LEVEL = KTX_PACK_UASTC_LEVEL_VERYSLOW, + /*!< Maximum supported quality level. */ + KTX_PACK_UASTC_LEVEL_MASK = 0xF, + /*!< Mask to extract the level from the other bits. */ + KTX_PACK_UASTC_FAVOR_UASTC_ERROR = 8, + /*!< Optimize for lowest UASTC error. */ + KTX_PACK_UASTC_FAVOR_BC7_ERROR = 16, + /*!< Optimize for lowest BC7 error. */ + KTX_PACK_UASTC_ETC1_FASTER_HINTS = 64, + /*!< Optimize for faster transcoding to ETC1. */ + KTX_PACK_UASTC_ETC1_FASTEST_HINTS = 128, + /*!< Optimize for fastest transcoding to ETC1. */ + KTX_PACK_UASTC__ETC1_DISABLE_FLIP_AND_INDIVIDUAL = 256 + /*!< Not documented in BasisU code. */ +} ktx_pack_uastc_flag_bits_e; +typedef ktx_uint32_t ktx_pack_uastc_flags; + +/** + * @~English + * @brief Options specifiying ASTC encoding quality levels. + */ +typedef enum ktx_pack_astc_quality_levels_e { + KTX_PACK_ASTC_QUALITY_LEVEL_FASTEST = 0, + /*!< Fastest compression. */ + KTX_PACK_ASTC_QUALITY_LEVEL_FAST = 10, + /*!< Fast compression. */ + KTX_PACK_ASTC_QUALITY_LEVEL_MEDIUM = 60, + /*!< Medium compression. */ + KTX_PACK_ASTC_QUALITY_LEVEL_THOROUGH = 98, + /*!< Slower compression. */ + KTX_PACK_ASTC_QUALITY_LEVEL_EXHAUSTIVE = 100, + /*!< Very slow compression. */ + KTX_PACK_ASTC_QUALITY_LEVEL_MAX = KTX_PACK_ASTC_QUALITY_LEVEL_EXHAUSTIVE, + /*!< Maximum supported quality level. */ +} ktx_pack_astc_quality_levels_e; + +/** + * @~English + * @brief Options specifiying ASTC encoding block dimensions + */ +typedef enum ktx_pack_astc_block_dimension_e { + // 2D formats + KTX_PACK_ASTC_BLOCK_DIMENSION_4x4, //: 8.00 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_5x4, //: 6.40 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_5x5, //: 5.12 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_6x5, //: 4.27 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_6x6, //: 3.56 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_8x5, //: 3.20 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_8x6, //: 2.67 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_10x5, //: 2.56 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_10x6, //: 2.13 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_8x8, //: 2.00 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_10x8, //: 1.60 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_10x10, //: 1.28 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_12x10, //: 1.07 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_12x12, //: 0.89 bpp + // 3D formats + KTX_PACK_ASTC_BLOCK_DIMENSION_3x3x3, //: 4.74 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_4x3x3, //: 3.56 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_4x4x3, //: 2.67 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_4x4x4, //: 2.00 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_5x4x4, //: 1.60 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_5x5x4, //: 1.28 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_5x5x5, //: 1.02 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_6x5x5, //: 0.85 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_6x6x5, //: 0.71 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_6x6x6, //: 0.59 bpp + KTX_PACK_ASTC_BLOCK_DIMENSION_MAX = KTX_PACK_ASTC_BLOCK_DIMENSION_6x6x6 + /*!< Maximum supported blocks. */ +} ktx_pack_astc_block_dimension_e; + +/** + * @~English + * @brief Options specifying ASTC encoder profile mode + * This and function is used later to derive the profile. + */ +typedef enum ktx_pack_astc_encoder_mode_e { + KTX_PACK_ASTC_ENCODER_MODE_DEFAULT, + KTX_PACK_ASTC_ENCODER_MODE_LDR, + KTX_PACK_ASTC_ENCODER_MODE_HDR, + KTX_PACK_ASTC_ENCODER_MODE_MAX = KTX_PACK_ASTC_ENCODER_MODE_HDR +} ktx_pack_astc_encoder_mode_e; + +extern KTX_API const ktx_uint32_t KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL; + +/** + * @memberof ktxTexture + * @~English + * @brief Structure for passing extended parameters to + * ktxTexture_CompressAstc. + * + * Passing a struct initialized to 0 (e.g. " = {0};") will use blockDimension + * 4x4, mode LDR and qualityLevel FASTEST. Setting qualityLevel to + * KTX_PACK_ASTC_QUALITY_LEVEL_MEDIUM is recommended. + */ +typedef struct ktxAstcParams { + ktx_uint32_t structSize; + /*!< Size of this struct. Used so library can tell which version + of struct is being passed. + */ + + ktx_bool_t verbose; + /*!< If true, prints Astc encoder operation details to + @c stdout. Not recommended for GUI apps. + */ + + ktx_uint32_t threadCount; + /*!< Number of threads used for compression. Default is 1. + */ + + /* astcenc params */ + ktx_uint32_t blockDimension; + /*!< Combinations of block dimensions that astcenc supports + i.e. 6x6, 8x8, 6x5 etc + */ + + ktx_uint32_t mode; + /*!< Can be {ldr/hdr} from astcenc + */ + + ktx_uint32_t qualityLevel; + /*!< astcenc supports -fastest, -fast, -medium, -thorough, -exhaustive + */ + + ktx_bool_t normalMap; + /*!< Tunes codec parameters for better quality on normal maps + In this mode normals are compressed to X,Y components + Discarding Z component, reader will need to generate Z + component in shaders. + */ + + ktx_bool_t perceptual; + /*!< The codec should optimize for perceptual error, instead of direct + RMS error. This aims to improves perceived image quality, but + typically lowers the measured PSNR score. Perceptual methods are + currently only available for normal maps and RGB color data. + */ + + char inputSwizzle[4]; + /*!< A swizzle to provide as input to astcenc. It must match the regular + expression /^[rgba01]{4}$/. + */ +} ktxAstcParams; + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CompressAstcEx(ktxTexture2* This, ktxAstcParams* params); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CompressAstc(ktxTexture2* This, ktx_uint32_t quality); + +/** + * @memberof ktxTexture2 + * @~English + * @brief Structure for passing extended parameters to + * ktxTexture2_CompressBasisEx(). + * + * If you only want default values, use ktxTexture2_CompressBasis(). Here, at a minimum you + * must initialize the structure as follows: + * @code + * ktxBasisParams params = {0}; + * params.structSize = sizeof(params); + * params.compressionLevel = KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL; + * @endcode + * + * @e compressionLevel has to be explicitly set because 0 is a valid @e compressionLevel + * but is not the default used by the BasisU encoder when no value is set. Only the other + * settings that are to be non-default must be non-zero. + */ +typedef struct ktxBasisParams { + ktx_uint32_t structSize; + /*!< Size of this struct. Used so library can tell which version + of struct is being passed. + */ + ktx_bool_t uastc; + /*!< True to use UASTC base, false to use ETC1S base. */ + ktx_bool_t verbose; + /*!< If true, prints Basis Universal encoder operation details to + @c stdout. Not recommended for GUI apps. + */ + ktx_bool_t noSSE; + /*!< True to forbid use of the SSE instruction set. Ignored if CPU + does not support SSE. */ + ktx_uint32_t threadCount; + /*!< Number of threads used for compression. Default is 1. */ + + /* ETC1S params */ + + ktx_uint32_t compressionLevel; + /*!< Encoding speed vs. quality tradeoff. Range is [0,5]. Higher values + are slower, but give higher quality. There is no default. Callers + must explicitly set this value. Callers can use + KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL as a default value. + Currently this is 2. + */ + ktx_uint32_t qualityLevel; + /*!< Compression quality. Range is [1,255]. Lower gives better + compression/lower quality/faster. Higher gives less compression + /higher quality/slower. This automatically determines values for + @c maxEndpoints, @c maxSelectors, + @c endpointRDOThreshold and @c selectorRDOThreshold + for the target quality level. Setting these parameters overrides + the values determined by @c qualityLevel which defaults to + 128 if neither it nor both of @c maxEndpoints and + @c maxSelectors have been set. + @note @e Both of @c maxEndpoints and @c maxSelectors + must be set for them to have any effect. + @note qualityLevel will only determine values for + @c endpointRDOThreshold and @c selectorRDOThreshold + when its value exceeds 128, otherwise their defaults will be used. + */ + ktx_uint32_t maxEndpoints; + /*!< Manually set the max number of color endpoint clusters. + Range is [1,16128]. Default is 0, unset. If this is set, maxSelectors + must also be set, otherwise the value will be ignored. + */ + float endpointRDOThreshold; + /*!< Set endpoint RDO quality threshold. The default is 1.25. Lower is + higher quality but less quality per output bit (try [1.0,3.0]. + This will override the value chosen by @c qualityLevel. + */ + ktx_uint32_t maxSelectors; + /*!< Manually set the max number of color selector clusters. Range + is [1,16128]. Default is 0, unset. If this is set, maxEndpoints + must also be set, otherwise the value will be ignored. + */ + float selectorRDOThreshold; + /*!< Set selector RDO quality threshold. The default is 1.5. Lower is + higher quality but less quality per output bit (try [1.0,3.0]). + This will override the value chosen by @c qualityLevel. + */ + char inputSwizzle[4]; + /*!< A swizzle to apply before encoding. It must match the regular + expression /^[rgba01]{4}$/. If both this and preSwizzle + are specified ktxTexture_CompressBasisEx will raise + KTX_INVALID_OPERATION. + */ + ktx_bool_t normalMap; + /*!< Tunes codec parameters for better quality on normal maps (no + selector RDO, no endpoint RDO) and sets the texture's DFD appropriately. + Only valid for linear textures. + */ + ktx_bool_t separateRGToRGB_A; + /*!< @deprecated. This was and is a no-op. 2-component inputs have always been + automatically separated using an "rrrg" inputSwizzle. @sa inputSwizzle and normalMode. + */ + ktx_bool_t preSwizzle; + /*!< If the texture has @c KTXswizzle metadata, apply it before + compressing. Swizzling, like @c rabb may yield drastically + different error metrics if done after supercompression. + */ + ktx_bool_t noEndpointRDO; + /*!< Disable endpoint rate distortion optimizations. Slightly faster, + less noisy output, but lower quality per output bit. Default is + KTX_FALSE. + */ + ktx_bool_t noSelectorRDO; + /*!< Disable selector rate distortion optimizations. Slightly faster, + less noisy output, but lower quality per output bit. Default is + KTX_FALSE. + */ + + /* UASTC params */ + + ktx_pack_uastc_flags uastcFlags; + /*!< A set of ::ktx_pack_uastc_flag_bits_e controlling UASTC + encoding. The most important value is the level given in the + least-significant 4 bits which selects a speed vs quality tradeoff + as shown in the following table: + + Level/Speed | Quality + :-----: | :-------: + KTX_PACK_UASTC_LEVEL_FASTEST | 43.45dB + KTX_PACK_UASTC_LEVEL_FASTER | 46.49dB + KTX_PACK_UASTC_LEVEL_DEFAULT | 47.47dB + KTX_PACK_UASTC_LEVEL_SLOWER | 48.01dB + KTX_PACK_UASTC_LEVEL_VERYSLOW | 48.24dB + */ + ktx_bool_t uastcRDO; + /*!< Enable Rate Distortion Optimization (RDO) post-processing. + */ + float uastcRDOQualityScalar; + /*!< UASTC RDO quality scalar (lambda). Lower values yield higher + quality/larger LZ compressed files, higher values yield lower + quality/smaller LZ compressed files. A good range to try is [.2,4]. + Full range is [.001,50.0]. Default is 1.0. + */ + ktx_uint32_t uastcRDODictSize; + /*!< UASTC RDO dictionary size in bytes. Default is 4096. Lower + values=faster, but give less compression. Range is [64,65536]. + */ + float uastcRDOMaxSmoothBlockErrorScale; + /*!< UASTC RDO max smooth block error scale. Range is [1,300]. + Default is 10.0, 1.0 is disabled. Larger values suppress more + artifacts (and allocate more bits) on smooth blocks. + */ + float uastcRDOMaxSmoothBlockStdDev; + /*!< UASTC RDO max smooth block standard deviation. Range is + [.01,65536.0]. Default is 18.0. Larger values expand the range of + blocks considered smooth. + */ + ktx_bool_t uastcRDODontFavorSimplerModes; + /*!< Do not favor simpler UASTC modes in RDO mode. + */ + ktx_bool_t uastcRDONoMultithreading; + /*!< Disable RDO multithreading (slightly higher compression, + deterministic). + */ + +} ktxBasisParams; + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CompressBasisEx(ktxTexture2* This, ktxBasisParams* params); + +/** + * @~English + * @brief Enumerators for specifying the transcode target format. + * + * For BasisU/ETC1S format, @e Opaque and @e alpha here refer to 2 separate + * RGB images, a.k.a slices within the BasisU compressed data. For UASTC + * format they refer to the RGB and the alpha components of the UASTC data. If + * the original image had only 2 components, R will be in the opaque portion + * and G in the alpha portion. The R value will be replicated in the RGB + * components. In the case of BasisU the G value will be replicated in all 3 + * components of the alpha slice. If the original image had only 1 component + * it's value is replicated in all 3 components of the opaque portion and + * there is no alpha. + * + * @note You should not transcode sRGB encoded data to @c KTX_TTF_BC4_R, + * @c KTX_TTF_BC5_RG, @c KTX_TTF_ETC2_EAC_R{,G}11, @c KTX_TTF_RGB565, + * @c KTX_TTF_BGR565 or @c KTX_TTF_RGBA4444 formats as neither OpenGL nor + * Vulkan support sRGB variants of these. Doing sRGB decoding in the shader + * will not produce correct results if any texture filtering is being used. + */ +typedef enum ktx_transcode_fmt_e { + // Compressed formats + + // ETC1-2 + KTX_TTF_ETC1_RGB = 0, + /*!< Opaque only. Returns RGB or alpha data, if + KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is + specified. */ + KTX_TTF_ETC2_RGBA = 1, + /*!< Opaque+alpha. EAC_A8 block followed by an ETC1 block. The + alpha channel will be opaque for textures without an alpha + channel. */ + + // BC1-5, BC7 (desktop, some mobile devices) + KTX_TTF_BC1_RGB = 2, + /*!< Opaque only, no punchthrough alpha support yet. Returns RGB + or alpha data, if KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS + flag is specified. */ + KTX_TTF_BC3_RGBA = 3, + /*!< Opaque+alpha. BC4 block with alpha followed by a BC1 block. The + alpha channel will be opaque for textures without an alpha + channel. */ + KTX_TTF_BC4_R = 4, + /*!< One BC4 block. R = opaque.g or alpha.g, if + KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is + specified. */ + KTX_TTF_BC5_RG = 5, + /*!< Two BC4 blocks, R=opaque.g and G=alpha.g The texture should + have an alpha channel (if not G will be all 255's. For tangent + space normal maps. */ + KTX_TTF_BC7_RGBA = 6, + /*!< RGB or RGBA mode 5 for ETC1S, modes 1, 2, 3, 4, 5, 6, 7 for + UASTC. */ + + // PVRTC1 4bpp (mobile, PowerVR devices) + KTX_TTF_PVRTC1_4_RGB = 8, + /*!< Opaque only. Returns RGB or alpha data, if + KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is + specified. */ + KTX_TTF_PVRTC1_4_RGBA = 9, + /*!< Opaque+alpha. Most useful for simple opacity maps. If the + texture doesn't have an alpha channel KTX_TTF_PVRTC1_4_RGB + will be used instead. Lowest quality of any supported + texture format. */ + + // ASTC (mobile, Intel devices, hopefully all desktop GPU's one day) + KTX_TTF_ASTC_4x4_RGBA = 10, + /*!< Opaque+alpha, ASTC 4x4. The alpha channel will be opaque for + textures without an alpha channel. The transcoder uses + RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and + [0,255]) endpoint precisions. */ + + // ATC and FXT1 formats are not supported by KTX2 as there + // are no equivalent VkFormats. + + KTX_TTF_PVRTC2_4_RGB = 18, + /*!< Opaque-only. Almost BC1 quality, much faster to transcode + and supports arbitrary texture dimensions (unlike + PVRTC1 RGB). */ + KTX_TTF_PVRTC2_4_RGBA = 19, + /*!< Opaque+alpha. Slower to transcode than cTFPVRTC2_4_RGB. + Premultiplied alpha is highly recommended, otherwise the + color channel can leak into the alpha channel on transparent + blocks. */ + + KTX_TTF_ETC2_EAC_R11 = 20, + /*!< R only (ETC2 EAC R11 unsigned). R = opaque.g or alpha.g, if + KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is + specified. */ + KTX_TTF_ETC2_EAC_RG11 = 21, + /*!< RG only (ETC2 EAC RG11 unsigned), R=opaque.g, G=alpha.g. The + texture should have an alpha channel (if not G will be all + 255's. For tangent space normal maps. */ + + // Uncompressed (raw pixel) formats + KTX_TTF_RGBA32 = 13, + /*!< 32bpp RGBA image stored in raster (not block) order in + memory, R is first byte, A is last byte. */ + KTX_TTF_RGB565 = 14, + /*!< 16bpp RGB image stored in raster (not block) order in memory, + R at bit position 11. */ + KTX_TTF_BGR565 = 15, + /*!< 16bpp RGB image stored in raster (not block) order in memory, + R at bit position 0. */ + KTX_TTF_RGBA4444 = 16, + /*!< 16bpp RGBA image stored in raster (not block) order in memory, + R at bit position 12, A at bit position 0. */ + + // Values for automatic selection of RGB or RGBA depending if alpha + // present. + KTX_TTF_ETC = 22, + /*!< Automatically selects @c KTX_TTF_ETC1_RGB or + @c KTX_TTF_ETC2_RGBA according to presence of alpha. */ + KTX_TTF_BC1_OR_3 = 23, + /*!< Automatically selects @c KTX_TTF_BC1_RGB or + @c KTX_TTF_BC3_RGBA according to presence of alpha. */ + + KTX_TTF_NOSELECTION = 0x7fffffff, + + // Old enums for compatibility with code compiled against previous + // versions of libktx. + KTX_TF_ETC1 = KTX_TTF_ETC1_RGB, + //!< @deprecated. Use #KTX_TTF_ETC1_RGB. + KTX_TF_ETC2 = KTX_TTF_ETC, + //!< @deprecated. Use #KTX_TTF_ETC. + KTX_TF_BC1 = KTX_TTF_BC1_RGB, + //!< @deprecated. Use #KTX_TTF_BC1_RGB. + KTX_TF_BC3 = KTX_TTF_BC3_RGBA, + //!< @deprecated. Use #KTX_TTF_BC3_RGBA. + KTX_TF_BC4 = KTX_TTF_BC4_R, + //!< @deprecated. Use #KTX_TTF_BC4_R. + KTX_TF_BC5 = KTX_TTF_BC5_RG, + //!< @deprecated. Use #KTX_TTF_BC5_RG. + KTX_TTF_BC7_M6_RGB = KTX_TTF_BC7_RGBA, + //!< @deprecated. Use #KTX_TTF_BC7_RGBA. + KTX_TTF_BC7_M5_RGBA = KTX_TTF_BC7_RGBA, + //!< @deprecated. Use #KTX_TTF_BC7_RGBA. + KTX_TF_BC7_M6_OPAQUE_ONLY = KTX_TTF_BC7_RGBA, + //!< @deprecated. Use #KTX_TTF_BC7_RGBA + KTX_TF_PVRTC1_4_OPAQUE_ONLY = KTX_TTF_PVRTC1_4_RGB + //!< @deprecated. Use #KTX_TTF_PVRTC1_4_RGB. +} ktx_transcode_fmt_e; + +/** + * @~English + * @brief Flags guiding transcoding of Basis Universal compressed textures. + */ +typedef enum ktx_transcode_flag_bits_e { + KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 = 2, + /*!< PVRTC1: decode non-pow2 ETC1S texture level to the next larger + power of 2 (not implemented yet, but we're going to support it). + Ignored if the slice's dimensions are already a power of 2. + */ + KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS = 4, + /*!< When decoding to an opaque texture format, if the Basis data has + alpha, decode the alpha slice instead of the color slice to the + output texture format. Has no effect if there is no alpha data. + */ + KTX_TF_HIGH_QUALITY = 32, + /*!< Request higher quality transcode of UASTC to BC1, BC3, ETC2_EAC_R11 and + ETC2_EAC_RG11. The flag is unused by other UASTC transcoders. + */ +} ktx_transcode_flag_bits_e; +typedef ktx_uint32_t ktx_transcode_flags; + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_TranscodeBasis(ktxTexture2* This, ktx_transcode_fmt_e fmt, + ktx_transcode_flags transcodeFlags); + +/* + * Returns a string corresponding to a KTX error code. + */ +KTX_API const char* KTX_APIENTRY +ktxErrorString(KTX_error_code error); + +/* + * Returns a string corresponding to a supercompression scheme. + */ +KTX_API const char* KTX_APIENTRY +ktxSupercompressionSchemeString(ktxSupercmpScheme scheme); + +/* + * Returns a string corresponding to a transcode target format. + */ +KTX_API const char* KTX_APIENTRY +ktxTranscodeFormatString(ktx_transcode_fmt_e format); + +KTX_API KTX_error_code KTX_APIENTRY ktxHashList_Create(ktxHashList** ppHl); +KTX_API KTX_error_code KTX_APIENTRY +ktxHashList_CreateCopy(ktxHashList** ppHl, ktxHashList orig); +KTX_API void KTX_APIENTRY ktxHashList_Construct(ktxHashList* pHl); +KTX_API void KTX_APIENTRY +ktxHashList_ConstructCopy(ktxHashList* pHl, ktxHashList orig); +KTX_API void KTX_APIENTRY ktxHashList_Destroy(ktxHashList* head); +KTX_API void KTX_APIENTRY ktxHashList_Destruct(ktxHashList* head); + +/* + * Adds a key-value pair to a hash list. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashList_AddKVPair(ktxHashList* pHead, const char* key, + unsigned int valueLen, const void* value); + +/* + * Deletes a ktxHashListEntry from a ktxHashList. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashList_DeleteEntry(ktxHashList* pHead, ktxHashListEntry* pEntry); + +/* + * Finds the entry for a key in a ktxHashList and deletes it. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashList_DeleteKVPair(ktxHashList* pHead, const char* key); + +/* + * Looks up a key and returns the ktxHashListEntry. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashList_FindEntry(ktxHashList* pHead, const char* key, + ktxHashListEntry** ppEntry); + +/* + * Looks up a key and returns the value. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashList_FindValue(ktxHashList* pHead, const char* key, + unsigned int* pValueLen, void** pValue); + +/* + * Return the next entry in a ktxHashList. + */ +KTX_API ktxHashListEntry* KTX_APIENTRY +ktxHashList_Next(ktxHashListEntry* entry); + +/* + * Sorts a ktxHashList into order of the key codepoints. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashList_Sort(ktxHashList* pHead); + +/* + * Serializes a ktxHashList to a block of memory suitable for + * writing to a KTX file. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashList_Serialize(ktxHashList* pHead, + unsigned int* kvdLen, unsigned char** kvd); + +/* + * Creates a hash table from the serialized data read from a + * a KTX file. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashList_Deserialize(ktxHashList* pHead, unsigned int kvdLen, void* kvd); + +/* + * Get the key from a ktxHashListEntry + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashListEntry_GetKey(ktxHashListEntry* This, + unsigned int* pKeyLen, char** ppKey); + +/* + * Get the value from a ktxHashListEntry + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxHashListEntry_GetValue(ktxHashListEntry* This, + unsigned int* pValueLen, void** ppValue); + +/*===========================================================* + * Utilities for printing info about a KTX file. * + *===========================================================*/ + +KTX_API KTX_error_code KTX_APIENTRY ktxPrintInfoForStdioStream(FILE* stdioStream); +KTX_API KTX_error_code KTX_APIENTRY ktxPrintInfoForNamedFile(const char* const filename); +KTX_API KTX_error_code KTX_APIENTRY ktxPrintInfoForMemory(const ktx_uint8_t* bytes, ktx_size_t size); + +#ifdef __cplusplus +} +#endif + +/*========================================================================* + * For backward compatibilty with the V3 & early versions of the V4 APIs. * + *========================================================================*/ + +/** + * @deprecated Will be dropped before V4 release. + */ +#define ktx_texture_transcode_fmt_e ktx_transcode_fmt_e + +/** + * @deprecated Will be dropped before V4 release. + */ +#define ktx_texture_decode_flags ktx_transcode_flag_bits + +/** + * @deprecated Will be dropped before V4 release. + */ +#define ktxTexture_GetSize ktxTexture_GetDatasize + +/** +@~English +@page libktx_history Revision History + +@section v8 Version 4.0 +Added: +@li Support for KTX Version 2. +@li Support for encoding and transcoding Basis Universal images in KTX Version 2 files. +@li Function to print info about a KTX file. + +@section v7 Version 3.0.1 +Fixed: +@li GitHub issue #159: compile failure with recent Vulkan SDKs. +@li Incorrect mapping of GL DXT3 and DXT5 formats to Vulkan equivalents. +@li Incorrect BC4 blocksize. +@li Missing mapping of PVRTC formats from GL to Vulkan. +@li Incorrect block width and height calculations for sizes that are not + a multiple of the block size. +@li Incorrect KTXorientation key in test images. + +@section v6 Version 3.0 +Added: +@li new ktxTexture object based API for reading KTX files without an OpenGL context. +@li Vulkan loader. @#include <ktxvulkan.h> to use it. + +Changed: +@li ktx.h to not depend on KHR/khrplatform.h and GL{,ES*}/gl{corearb,}.h. + Applications using OpenGL must now include these files themselves. +@li ktxLoadTexture[FMN], removing the hack of loading 1D textures as 2D textures + when the OpenGL context does not support 1D textures. + KTX_UNSUPPORTED_TEXTURE_TYPE is now returned. + +@section v5 Version 2.0.2 +Added: +@li Support for cubemap arrays. + +Changed: +@li New build system + +Fixed: +@li GitHub issue #40: failure to byte-swap key-value lengths. +@li GitHub issue #33: returning incorrect target when loading cubemaps. +@li GitHub PR #42: loading of texture arrays. +@li GitHub PR #41: compilation error when KTX_OPENGL_ES2=1 defined. +@li GitHub issue #39: stack-buffer-overflow in toktx +@li Don't use GL_EXTENSIONS on recent OpenGL versions. + +@section v4 Version 2.0.1 +Added: +@li CMake build files. Thanks to Pavel Rotjberg for the initial version. + +Changed: +@li ktxWriteKTXF to check the validity of the type & format combinations + passed to it. + +Fixed: +@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=999">999</a>: 16-bit luminance texture cannot be written. +@li compile warnings from compilers stricter than MS Visual C++. Thanks to + Pavel Rotjberg. + +@section v3 Version 2.0 +Added: +@li support for decoding ETC2 and EAC formats in the absence of a hardware + decoder. +@li support for converting textures with legacy LUMINANCE, LUMINANCE_ALPHA, + etc. formats to the equivalent R, RG, etc. format with an + appropriate swizzle, when loading in OpenGL Core Profile contexts. +@li ktxErrorString function to return a string corresponding to an error code. +@li tests for ktxLoadTexture[FN] that run under OpenGL ES 3.0 and OpenGL 3.3. + The latter includes an EGL on WGL wrapper that makes porting apps between + OpenGL ES and OpenGL easier on Windows. +@li more texture formats to ktxLoadTexture[FN] and toktx tests. + +Changed: +@li ktxLoadTexture[FMN] to discover the capabilities of the GL context at + run time and load textures, or not, according to those capabilities. + +Fixed: +@li failure of ktxWriteKTXF to pad image rows to 4 bytes as required by the KTX + format. +@li ktxWriteKTXF exiting with KTX_FILE_WRITE_ERROR when attempting to write + more than 1 byte of face-LOD padding. + +Although there is only a very minor API change, the addition of ktxErrorString, +the functional changes are large enough to justify bumping the major revision +number. + +@section v2 Version 1.0.1 +Implemented ktxLoadTextureM. +Fixed the following: +@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=571">571</a>: crash when null passed for pIsMipmapped. +@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=572">572</a>: memory leak when unpacking ETC textures. +@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=573">573</a>: potential crash when unpacking ETC textures with unused padding pixels. +@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=576">576</a>: various small fixes. + +Thanks to Krystian Bigaj for the ktxLoadTextureM implementation and these fixes. + +@section v1 Version 1.0 +Initial release. + +*/ + +#endif /* KTX_H_A55A6F00956F42F3A137C11929827FE1 */ diff --git a/thirdparty/libktx/include/ktxvulkan.h b/thirdparty/libktx/include/ktxvulkan.h new file mode 100644 index 0000000000..e695ee6863 --- /dev/null +++ b/thirdparty/libktx/include/ktxvulkan.h @@ -0,0 +1,253 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +#ifndef KTX_H_C54B42AEE39611E68E1E4FF8C51D1C66 +#define KTX_H_C54B42AEE39611E68E1E4FF8C51D1C66 + +/* + * Copyright 2017-2020 The Khronos Group, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file + * @~English + * + * @brief Declares the public functions and structures of the + * KTX Vulkan texture loading API. + * + * A separate header file is used to avoid extra dependencies for those not + * using Vulkan. The nature of the Vulkan API, rampant structures and enums, + * means that vulkan.h must be included @e before including this file. The + * alternative is duplicating unattractively large parts of it. + * + * @author Mark Callow, Edgewise Consulting + * + * $Date$ + */ + +#include <ktx.h> + +#if 0 +/* Avoid Vulkan include file */ +#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; + +#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; +#else + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#endif + +VK_DEFINE_HANDLE(VkPhysicalDevice) +VK_DEFINE_HANDLE(VkDevice) +VK_DEFINE_HANDLE(VkQueue) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCommandPool) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSampler) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @struct ktxVulkanFunctions + * @~English + * @brief Struct for applications to pass Vulkan function pointers to the + * ktxTexture_VkUpload functions via a ktxVulkanDeviceInfo struct. + * + * @c vkGetInstanceProcAddr and @c vkGetDeviceProcAddr should be set, others + * are optional. + */ +typedef struct ktxVulkanFunctions { + // These are functions pointers we need to perform our vulkan duties. + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; + + // These we optionally specify + PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; + PFN_vkAllocateMemory vkAllocateMemory; + PFN_vkBeginCommandBuffer vkBeginCommandBuffer; + PFN_vkBindBufferMemory vkBindBufferMemory; + PFN_vkBindImageMemory vkBindImageMemory; + PFN_vkCmdBlitImage vkCmdBlitImage; + PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; + PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; + PFN_vkCreateImage vkCreateImage; + PFN_vkDestroyImage vkDestroyImage; + PFN_vkCreateBuffer vkCreateBuffer; + PFN_vkDestroyBuffer vkDestroyBuffer; + PFN_vkCreateFence vkCreateFence; + PFN_vkDestroyFence vkDestroyFence; + PFN_vkEndCommandBuffer vkEndCommandBuffer; + PFN_vkFreeCommandBuffers vkFreeCommandBuffers; + PFN_vkFreeMemory vkFreeMemory; + PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; + PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; + PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout; + PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties; + PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties; + PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; + PFN_vkMapMemory vkMapMemory; + PFN_vkQueueSubmit vkQueueSubmit; + PFN_vkQueueWaitIdle vkQueueWaitIdle; + PFN_vkUnmapMemory vkUnmapMemory; + PFN_vkWaitForFences vkWaitForFences; +} ktxVulkanFunctions; + +/** + * @class ktxVulkanTexture + * @~English + * @brief Struct for returning information about the Vulkan texture image + * created by the ktxTexture_VkUpload* functions. + * + * Creation of these objects is internal to the upload functions. + */ +typedef struct ktxVulkanTexture +{ + PFN_vkDestroyImage vkDestroyImage; /*!< Pointer to vkDestroyImage function */ + PFN_vkFreeMemory vkFreeMemory; /*!< Pointer to vkFreeMemory function */ + + VkImage image; /*!< Handle to the Vulkan image created by the loader. */ + VkFormat imageFormat; /*!< Format of the image data. */ + VkImageLayout imageLayout; /*!< Layout of the created image. Has the same + value as @p layout parameter passed to the + loader. */ + VkDeviceMemory deviceMemory; /*!< The memory allocated for the image on + the Vulkan device. */ + VkImageViewType viewType; /*!< ViewType corresponding to @p image. Reflects + the dimensionality, cubeness and arrayness + of the image. */ + uint32_t width; /*!< The width of the image. */ + uint32_t height; /*!< The height of the image. */ + uint32_t depth; /*!< The depth of the image. */ + uint32_t levelCount; /*!< The number of MIP levels in the image. */ + uint32_t layerCount; /*!< The number of array layers in the image. */ +} ktxVulkanTexture; + +KTX_API void KTX_APIENTRY +ktxVulkanTexture_Destruct(ktxVulkanTexture* This, VkDevice device, + const VkAllocationCallbacks* pAllocator); + + + + +/** + * @class ktxVulkanDeviceInfo + * @~English + * @brief Struct for passing information about the Vulkan device on which + * to create images to the texture image loading functions. + * + * Avoids passing a large number of parameters to each loading function. + * Use of ktxVulkanDeviceInfo_create() or ktxVulkanDeviceInfo_construct() to + * populate this structure is highly recommended. + * + * @code + ktxVulkanDeviceInfo vdi; + ktxVulkanTexture texture; + + vdi = ktxVulkanDeviceInfo_create(physicalDevice, + device, + queue, + cmdPool, + &allocator); + ktxLoadVkTextureN("texture_1.ktx", vdi, &texture, NULL, NULL); + // ... + ktxLoadVkTextureN("texture_n.ktx", vdi, &texture, NULL, NULL); + ktxVulkanDeviceInfo_destroy(vdi); + * @endcode + */ +typedef struct ktxVulkanDeviceInfo { + VkInstance instance; /*!< Instance used to communicate with vulkan. */ + VkPhysicalDevice physicalDevice; /*!< Handle of the physical device. */ + VkDevice device; /*!< Handle of the logical device. */ + VkQueue queue; /*!< Handle to the queue to which to submit commands. */ + VkCommandBuffer cmdBuffer; /*!< Handle of the cmdBuffer to use. */ + /** Handle of the command pool from which to allocate the command buffer. */ + VkCommandPool cmdPool; + /** Pointer to the allocator to use for the command buffer and created + * images. + */ + const VkAllocationCallbacks* pAllocator; + /** Memory properties of the Vulkan physical device. */ + VkPhysicalDeviceMemoryProperties deviceMemoryProperties; + + /** The functions needed to operate functions */ + ktxVulkanFunctions vkFuncs; +} ktxVulkanDeviceInfo; + + +KTX_API ktxVulkanDeviceInfo* KTX_APIENTRY +ktxVulkanDeviceInfo_CreateEx(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device, + VkQueue queue, VkCommandPool cmdPool, + const VkAllocationCallbacks* pAllocator, + const ktxVulkanFunctions* pFunctions); + +KTX_API ktxVulkanDeviceInfo* KTX_APIENTRY +ktxVulkanDeviceInfo_Create(VkPhysicalDevice physicalDevice, VkDevice device, + VkQueue queue, VkCommandPool cmdPool, + const VkAllocationCallbacks* pAllocator); + +KTX_API KTX_error_code KTX_APIENTRY +ktxVulkanDeviceInfo_Construct(ktxVulkanDeviceInfo* This, + VkPhysicalDevice physicalDevice, VkDevice device, + VkQueue queue, VkCommandPool cmdPool, + const VkAllocationCallbacks* pAllocator); + +KTX_API KTX_error_code KTX_APIENTRY +ktxVulkanDeviceInfo_ConstructEx(ktxVulkanDeviceInfo* This, + VkInstance instance, + VkPhysicalDevice physicalDevice, VkDevice device, + VkQueue queue, VkCommandPool cmdPool, + const VkAllocationCallbacks* pAllocator, + const ktxVulkanFunctions* pFunctions); + +KTX_API void KTX_APIENTRY +ktxVulkanDeviceInfo_Destruct(ktxVulkanDeviceInfo* This); +KTX_API void KTX_APIENTRY +ktxVulkanDeviceInfo_Destroy(ktxVulkanDeviceInfo* This); +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_VkUploadEx(ktxTexture* This, ktxVulkanDeviceInfo* vdi, + ktxVulkanTexture* vkTexture, + VkImageTiling tiling, + VkImageUsageFlags usageFlags, + VkImageLayout finalLayout); +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_VkUpload(ktxTexture* texture, ktxVulkanDeviceInfo* vdi, + ktxVulkanTexture *vkTexture); +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_VkUploadEx(ktxTexture1* This, ktxVulkanDeviceInfo* vdi, + ktxVulkanTexture* vkTexture, + VkImageTiling tiling, + VkImageUsageFlags usageFlags, + VkImageLayout finalLayout); +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_VkUpload(ktxTexture1* texture, ktxVulkanDeviceInfo* vdi, + ktxVulkanTexture *vkTexture); +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_VkUploadEx(ktxTexture2* This, ktxVulkanDeviceInfo* vdi, + ktxVulkanTexture* vkTexture, + VkImageTiling tiling, + VkImageUsageFlags usageFlags, + VkImageLayout finalLayout); +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_VkUpload(ktxTexture2* texture, ktxVulkanDeviceInfo* vdi, + ktxVulkanTexture *vkTexture); + +KTX_API VkFormat KTX_APIENTRY +ktxTexture_GetVkFormat(ktxTexture* This); + +KTX_API VkFormat KTX_APIENTRY +ktxTexture1_GetVkFormat(ktxTexture1* This); + +KTX_API VkFormat KTX_APIENTRY +ktxTexture2_GetVkFormat(ktxTexture2* This); + +#ifdef __cplusplus +} +#endif + +#endif /* KTX_H_A55A6F00956F42F3A137C11929827FE1 */ diff --git a/thirdparty/libktx/lib/basis_sgd.h b/thirdparty/libktx/lib/basis_sgd.h new file mode 100644 index 0000000000..6c55909652 --- /dev/null +++ b/thirdparty/libktx/lib/basis_sgd.h @@ -0,0 +1,85 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab textwidth=70: */ + +/* + * Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file basisu_sgd.h + * @~English + * + * @brief Declare global data for Basis LZ supercompression with ETC1S. + * + * These functions are private and should not be used outside the library. + */ + +#ifndef _BASIS_SGD_H_ +#define _BASIS_SGD_H_ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// This must be the same value as cSliceDescFlagsFrameIsIFrame so we can just +// invert the bit when passing back & forth. As FrameIsIFrame is within +// a C namespace it can't easily be accessed from a c header. +enum bu_image_flags__bits_e { eBUImageIsPframe = 0x02 }; + +typedef uint32_t buFlags; + +typedef struct ktxBasisLzGlobalHeader { + uint16_t endpointCount; + uint16_t selectorCount; + uint32_t endpointsByteLength; + uint32_t selectorsByteLength; + uint32_t tablesByteLength; + uint32_t extendedByteLength; +} ktxBasisLzGlobalHeader; + +// This header is followed by imageCount "slice" descriptions. + +// 1, or 2 slices per image (i.e. layer, face & slice). +// These offsets are relative to start of a mip level as given by the +// main levelIndex. +typedef struct ktxBasisLzEtc1sImageDesc { + buFlags imageFlags; + uint32_t rgbSliceByteOffset; + uint32_t rgbSliceByteLength; + uint32_t alphaSliceByteOffset; + uint32_t alphaSliceByteLength; +} ktxBasisLzEtc1sImageDesc; + +#define BGD_ETC1S_IMAGE_DESCS(bgd) \ + reinterpret_cast<ktxBasisLzEtc1sImageDesc*>(bgd + sizeof(ktxBasisLzGlobalHeader)) + +// The are followed in the global data by these ... +// uint8_t[endpointsByteLength] endpointsData; +// uint8_t[selectorsByteLength] selectorsData; +// uint8_t[tablesByteLength] tablesData; + +#define BGD_ENDPOINTS_ADDR(bgd, imageCount) \ + (bgd + sizeof(ktxBasisLzGlobalHeader) + sizeof(ktxBasisLzEtc1sImageDesc) * imageCount) + +#define BGD_SELECTORS_ADDR(bgd, bgdh, imageCount) (BGD_ENDPOINTS_ADDR(bgd, imageCount) + bgdh.endpointsByteLength) + +#define BGD_TABLES_ADDR(bgd, bgdh, imageCount) (BGD_SELECTORS_ADDR(bgd, bgdh, imageCount) + bgdh.selectorsByteLength) + +#define BGD_EXTENDED_ADDR(bgd, bgdh, imageCount) (BGD_TABLES_ADDR(bgd, bgdh, imageCount) + bgdh.tablesByteLength) + +// Just because this is a convenient place to put it for basis_{en,trans}code. +enum alpha_content_e { + eNone, + eAlpha, + eGreen +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _BASIS_SGD_H_ */ diff --git a/thirdparty/libktx/lib/basis_transcode.cpp b/thirdparty/libktx/lib/basis_transcode.cpp new file mode 100644 index 0000000000..8df65bcb68 --- /dev/null +++ b/thirdparty/libktx/lib/basis_transcode.cpp @@ -0,0 +1,733 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file basis_transcode.cpp + * @~English + * + * @brief Functions for transcoding Basis Universal BasisLZ/ETC1S and UASTC textures. + * + * Two worlds collide here too. More uglyness! + * + * @author Mark Callow, www.edgewise-consulting.com + */ + +#include <inttypes.h> +#include <stdio.h> +#include <KHR/khr_df.h> + +#include "dfdutils/dfd.h" +#include "ktx.h" +#include "ktxint.h" +#include "texture2.h" +#include "vkformat_enum.h" +#include "vk_format.h" +#include "basis_sgd.h" +#include "transcoder/basisu_file_headers.h" +#include "transcoder/basisu_transcoder.h" +#include "transcoder/basisu_transcoder_internal.h" + +#undef DECLARE_PRIVATE +#undef DECLARE_PROTECTED +#define DECLARE_PRIVATE(n,t2) ktxTexture2_private& n = *(t2->_private) +#define DECLARE_PROTECTED(n,t2) ktxTexture_protected& n = *(t2->_protected) + +using namespace basisu; +using namespace basist; + +inline bool isPow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); } + +inline bool isPow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); } + +KTX_error_code +ktxTexture2_transcodeLzEtc1s(ktxTexture2* This, + alpha_content_e alphaContent, + ktxTexture2* prototype, + ktx_transcode_fmt_e outputFormat, + ktx_transcode_flags transcodeFlags); +KTX_error_code +ktxTexture2_transcodeUastc(ktxTexture2* This, + alpha_content_e alphaContent, + ktxTexture2* prototype, + ktx_transcode_fmt_e outputFormat, + ktx_transcode_flags transcodeFlags); + +/** + * @memberof ktxTexture2 + * @ingroup reader + * @~English + * @brief Transcode a KTX2 texture with BasisLZ/ETC1S or UASTC images. + * + * If the texture contains BasisLZ supercompressed images, Inflates them from + * back to ETC1S then transcodes them to the specified block-compressed + * format. If the texture contains UASTC images, inflates them, if they have been + * supercompressed with zstd, then transcodes then to the specified format, The + * transcoded images replace the original images and the texture's fields including + * the DFD are modified to reflect the new format. + * + * These types of textures must be transcoded to a desired target + * block-compressed format before they can be uploaded to a GPU via a + * graphics API. + * + * The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB, + * @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA, + * @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA, + * @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA, + * @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA, + * @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and + * @c KTX_TTF_BC1_OR_3. + * + * @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and + * @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3 + * does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if + * @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha + * channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected. + * + * Transcoding to ATC & FXT1 formats is not supported by libktx as there + * are no equivalent Vulkan formats. + * + * The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32, + * @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444. + * + * The following @p transcodeFlags are available. + * + * @sa ktxtexture2_CompressBasis(). + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum + * specifying the target format. + * @param[in] transcodeFlags bitfield of flags modifying the transcode + * operation. @sa ktx_texture_decode_flags_e. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_DATA_ERROR + * Supercompression global data is corrupted. + * @exception KTX_INVALID_OPERATION + * The texture's format is not transcodable (not + * ETC1S/BasisLZ or UASTC). + * @exception KTX_INVALID_OPERATION + * Supercompression global data is missing, i.e., + * the texture object is invalid. + * @exception KTX_INVALID_OPERATION + * Image data is missing, i.e., the texture object + * is invalid. + * @exception KTX_INVALID_OPERATION + * @p outputFormat is PVRTC1 but the texture does + * does not have power-of-two dimensions. + * @exception KTX_INVALID_VALUE @p outputFormat is invalid. + * @exception KTX_TRANSCODE_FAILED + * Something went wrong during transcoding. + * @exception KTX_UNSUPPORTED_FEATURE + * KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested + * or the specified transcode target has not been + * included in the library being used. + * @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding. + */ + KTX_error_code + ktxTexture2_TranscodeBasis(ktxTexture2* This, + ktx_transcode_fmt_e outputFormat, + ktx_transcode_flags transcodeFlags) +{ + uint32_t* BDB = This->pDfd + 1; + khr_df_model_e colorModel = (khr_df_model_e)KHR_DFDVAL(BDB, MODEL); + if (colorModel != KHR_DF_MODEL_UASTC + // Constructor has checked color model matches BASIS_LZ. + && This->supercompressionScheme != KTX_SS_BASIS_LZ) + { + return KTX_INVALID_OPERATION; // Not in a transcodable format. + } + + DECLARE_PRIVATE(priv, This); + if (This->supercompressionScheme == KTX_SS_BASIS_LZ) { + if (!priv._supercompressionGlobalData || priv._sgdByteLength == 0) + return KTX_INVALID_OPERATION; + } + + if (transcodeFlags & KTX_TF_PVRTC_DECODE_TO_NEXT_POW2) { + debug_printf("ktxTexture_TranscodeBasis: KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 currently unsupported\n"); + return KTX_UNSUPPORTED_FEATURE; + } + + if (outputFormat == KTX_TTF_PVRTC1_4_RGB + || outputFormat == KTX_TTF_PVRTC1_4_RGBA) { + if ((!isPow2(This->baseWidth)) || (!isPow2(This->baseHeight))) { + debug_printf("ktxTexture_TranscodeBasis: PVRTC1 only supports power of 2 dimensions\n"); + return KTX_INVALID_OPERATION; + } + } + + const bool srgb = (KHR_DFDVAL(BDB, TRANSFER) == KHR_DF_TRANSFER_SRGB); + alpha_content_e alphaContent = eNone; + if (colorModel == KHR_DF_MODEL_ETC1S) { + if (KHR_DFDSAMPLECOUNT(BDB) == 2) { + uint32_t channelId = KHR_DFDSVAL(BDB, 1, CHANNELID); + if (channelId == KHR_DF_CHANNEL_ETC1S_AAA) { + alphaContent = eAlpha; + } else if (channelId == KHR_DF_CHANNEL_ETC1S_GGG){ + alphaContent = eGreen; + } else { + return KTX_FILE_DATA_ERROR; + } + } + } else { + uint32_t channelId = KHR_DFDSVAL(BDB, 0, CHANNELID); + if (channelId == KHR_DF_CHANNEL_UASTC_RGBA) + alphaContent = eAlpha; + else if (channelId == KHR_DF_CHANNEL_UASTC_RRRG) + alphaContent = eGreen; + } + + VkFormat vkFormat; + + // Do some format mapping. + switch (outputFormat) { + case KTX_TTF_BC1_OR_3: + outputFormat = alphaContent != eNone ? KTX_TTF_BC3_RGBA + : KTX_TTF_BC1_RGB; + break; + case KTX_TTF_ETC: + outputFormat = alphaContent != eNone ? KTX_TTF_ETC2_RGBA + : KTX_TTF_ETC1_RGB; + break; + case KTX_TTF_PVRTC1_4_RGBA: + // This transcoder does not write opaque alpha blocks. + outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC1_4_RGBA + : KTX_TTF_PVRTC1_4_RGB; + break; + case KTX_TTF_PVRTC2_4_RGBA: + // This transcoder does not write opaque alpha blocks. + outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC2_4_RGBA + : KTX_TTF_PVRTC2_4_RGB; + break; + default: + /*NOP*/; + } + + switch (outputFormat) { + case KTX_TTF_ETC1_RGB: + vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK + : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; + break; + case KTX_TTF_ETC2_RGBA: + vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK + : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; + break; + case KTX_TTF_ETC2_EAC_R11: + vkFormat = VK_FORMAT_EAC_R11_UNORM_BLOCK; + break; + case KTX_TTF_ETC2_EAC_RG11: + vkFormat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK; + break; + case KTX_TTF_BC1_RGB: + // Transcoding doesn't support BC1 alpha. + vkFormat = srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK + : VK_FORMAT_BC1_RGB_UNORM_BLOCK; + break; + case KTX_TTF_BC3_RGBA: + vkFormat = srgb ? VK_FORMAT_BC3_SRGB_BLOCK + : VK_FORMAT_BC3_UNORM_BLOCK; + break; + case KTX_TTF_BC4_R: + vkFormat = VK_FORMAT_BC4_UNORM_BLOCK; + break; + case KTX_TTF_BC5_RG: + vkFormat = VK_FORMAT_BC5_UNORM_BLOCK; + break; + case KTX_TTF_PVRTC1_4_RGB: + case KTX_TTF_PVRTC1_4_RGBA: + vkFormat = srgb ? VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG + : VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG; + break; + case KTX_TTF_PVRTC2_4_RGB: + case KTX_TTF_PVRTC2_4_RGBA: + vkFormat = srgb ? VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG + : VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG; + break; + case KTX_TTF_BC7_RGBA: + vkFormat = srgb ? VK_FORMAT_BC7_SRGB_BLOCK + : VK_FORMAT_BC7_UNORM_BLOCK; + break; + case KTX_TTF_ASTC_4x4_RGBA: + vkFormat = srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK + : VK_FORMAT_ASTC_4x4_UNORM_BLOCK; + break; + case KTX_TTF_RGB565: + vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16; + break; + case KTX_TTF_BGR565: + vkFormat = VK_FORMAT_B5G6R5_UNORM_PACK16; + break; + case KTX_TTF_RGBA4444: + vkFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16; + break; + case KTX_TTF_RGBA32: + vkFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB + : VK_FORMAT_R8G8B8A8_UNORM; + break; + default: + return KTX_INVALID_VALUE; + } + + basis_tex_format textureFormat; + if (colorModel == KHR_DF_MODEL_UASTC) + textureFormat = basis_tex_format::cUASTC4x4; + else + textureFormat = basis_tex_format::cETC1S; + + if (!basis_is_format_supported((transcoder_texture_format)outputFormat, + textureFormat)) { + return KTX_UNSUPPORTED_FEATURE; + } + + + // Create a prototype texture to use for calculating sizes in the target + // format and, as useful side effects, provide us with a properly sized + // data allocation and the DFD for the target format. + ktxTextureCreateInfo createInfo; + createInfo.glInternalformat = 0; + createInfo.vkFormat = vkFormat; + createInfo.baseWidth = This->baseWidth; + createInfo.baseHeight = This->baseHeight; + createInfo.baseDepth = This->baseDepth; + createInfo.generateMipmaps = This->generateMipmaps; + createInfo.isArray = This->isArray; + createInfo.numDimensions = This->numDimensions; + createInfo.numFaces = This->numFaces; + createInfo.numLayers = This->numLayers; + createInfo.numLevels = This->numLevels; + createInfo.pDfd = nullptr; + + KTX_error_code result; + ktxTexture2* prototype; + result = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, + &prototype); + + if (result != KTX_SUCCESS) { + assert(result == KTX_OUT_OF_MEMORY); // The only run time error + return result; + } + + if (!This->pData) { + if (ktxTexture_isActiveStream((ktxTexture*)This)) { + // Load pending. Complete it. + result = ktxTexture2_LoadImageData(This, NULL, 0); + if (result != KTX_SUCCESS) + { + ktxTexture2_Destroy(prototype); + return result; + } + } else { + // No data to transcode. + ktxTexture2_Destroy(prototype); + return KTX_INVALID_OPERATION; + } + } + + // Transcoder global initialization. Requires ~9 milliseconds when compiled + // and executed natively on a Core i7 2.2 GHz. If this is too slow, the + // tables it computes can easily be moved to be compiled in. + static bool transcoderInitialized; + if (!transcoderInitialized) { + basisu_transcoder_init(); + transcoderInitialized = true; + } + + if (textureFormat == basis_tex_format::cETC1S) { + result = ktxTexture2_transcodeLzEtc1s(This, alphaContent, + prototype, outputFormat, + transcodeFlags); + } else { + result = ktxTexture2_transcodeUastc(This, alphaContent, + prototype, outputFormat, + transcodeFlags); + } + + if (result == KTX_SUCCESS) { + // Fix up the current texture + DECLARE_PROTECTED(thisPrtctd, This); + DECLARE_PRIVATE(protoPriv, prototype); + DECLARE_PROTECTED(protoPrtctd, prototype); + memcpy(&thisPrtctd._formatSize, &protoPrtctd._formatSize, + sizeof(ktxFormatSize)); + This->vkFormat = vkFormat; + This->isCompressed = prototype->isCompressed; + This->supercompressionScheme = KTX_SS_NONE; + priv._requiredLevelAlignment = protoPriv._requiredLevelAlignment; + // Copy the levelIndex from the prototype to This. + memcpy(priv._levelIndex, protoPriv._levelIndex, + This->numLevels * sizeof(ktxLevelIndexEntry)); + // Move the DFD and data from the prototype to This. + free(This->pDfd); + This->pDfd = prototype->pDfd; + prototype->pDfd = 0; + free(This->pData); + This->pData = prototype->pData; + This->dataSize = prototype->dataSize; + prototype->pData = 0; + prototype->dataSize = 0; + } + ktxTexture2_Destroy(prototype); + return result; + } + +/** + * @memberof ktxTexture2 @private + * @ingroup reader + * @~English + * @brief Transcode a KTX2 texture with BasisLZ supercompressed ETC1S images. + * + * Inflates the images from BasisLZ supercompression back to ETC1S + * then transcodes them to the specified block-compressed format. The + * transcoded images replace the original images and the texture's fields + * including the DFD are modified to reflect the new format. + * + * BasisLZ supercompressed textures must be transcoded to a desired target + * block-compressed format before they can be uploaded to a GPU via a graphics + * API. + * + * The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB, + * @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA, + * @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA, + * @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA, + * @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA, + * @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and + * @c KTX_TTF_BC1_OR_3. + * + * @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and + * @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3 + * does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if + * @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha + * channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected. + * + * ATC & FXT1 formats are not supported by KTX2 & libktx as there are no equivalent Vulkan formats. + * + * The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32, + * @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444. + * + * The following @p transcodeFlags are available. + * + * @sa ktxtexture2_CompressBasis(). + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum + * specifying the target format. + * @param[in] transcodeFlags bitfield of flags modifying the transcode + * operation. @sa ktx_texture_decode_flags_e. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_DATA_ERROR + * Supercompression global data is corrupted. + * @exception KTX_INVALID_OPERATION + * The texture's format is not transcodable (not + * ETC1S/BasisLZ or UASTC). + * @exception KTX_INVALID_OPERATION + * Supercompression global data is missing, i.e., + * the texture object is invalid. + * @exception KTX_INVALID_OPERATION + * Image data is missing, i.e., the texture object + * is invalid. + * @exception KTX_INVALID_OPERATION + * @p outputFormat is PVRTC1 but the texture does + * does not have power-of-two dimensions. + * @exception KTX_INVALID_VALUE @p outputFormat is invalid. + * @exception KTX_TRANSCODE_FAILED + * Something went wrong during transcoding. The + * texture object will be corrupted. + * @exception KTX_UNSUPPORTED_FEATURE + * KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested + * or the specified transcode target has not been + * included in the library being used. + * @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding. + */ +KTX_error_code +ktxTexture2_transcodeLzEtc1s(ktxTexture2* This, + alpha_content_e alphaContent, + ktxTexture2* prototype, + ktx_transcode_fmt_e outputFormat, + ktx_transcode_flags transcodeFlags) +{ + DECLARE_PRIVATE(priv, This); + DECLARE_PRIVATE(protoPriv, prototype); + KTX_error_code result = KTX_SUCCESS; + + assert(This->supercompressionScheme == KTX_SS_BASIS_LZ); + + uint8_t* bgd = priv._supercompressionGlobalData; + ktxBasisLzGlobalHeader& bgdh = *reinterpret_cast<ktxBasisLzGlobalHeader*>(bgd); + if (!(bgdh.endpointsByteLength && bgdh.selectorsByteLength && bgdh.tablesByteLength)) { + debug_printf("ktxTexture_TranscodeBasis: missing endpoints, selectors or tables"); + return KTX_FILE_DATA_ERROR; + } + + // Compute some helpful numbers. + // + // firstImages contains the indices of the first images for each level to + // ease finding the correct slice description when iterating from smallest + // level to largest or when randomly accessing them (t.b.c). The last array + // entry contains the total number of images, for calculating the offsets + // of the endpoints, etc. + uint32_t* firstImages = new uint32_t[This->numLevels+1]; + + // Temporary invariant value + uint32_t layersFaces = This->numLayers * This->numFaces; + firstImages[0] = 0; + for (uint32_t level = 1; level <= This->numLevels; level++) { + // NOTA BENE: numFaces * depth is only reasonable because they can't + // both be > 1. I.e there are no 3d cubemaps. + firstImages[level] = firstImages[level - 1] + + layersFaces * MAX(This->baseDepth >> (level - 1), 1); + } + uint32_t& imageCount = firstImages[This->numLevels]; + + if (BGD_TABLES_ADDR(0, bgdh, imageCount) + bgdh.tablesByteLength > priv._sgdByteLength) { + return KTX_FILE_DATA_ERROR; + } + // FIXME: Do more validation. + + // Prepare low-level transcoder for transcoding slices. + basist::basisu_lowlevel_etc1s_transcoder bit; + + // basisu_transcoder_state is used to find the previous frame when + // decoding a video P-Frame. It tracks the previous frame for each mip + // level. For cube map array textures we need to find the previous frame + // for each face so we a state per face. Although providing this is only + // needed for video, it is easier to always pass our own. + std::vector<basisu_transcoder_state> xcoderStates; + xcoderStates.resize(This->isVideo ? This->numFaces : 1); + + bit.decode_palettes(bgdh.endpointCount, BGD_ENDPOINTS_ADDR(bgd, imageCount), + bgdh.endpointsByteLength, + bgdh.selectorCount, BGD_SELECTORS_ADDR(bgd, bgdh, imageCount), + bgdh.selectorsByteLength); + + bit.decode_tables(BGD_TABLES_ADDR(bgd, bgdh, imageCount), + bgdh.tablesByteLength); + + // Find matching VkFormat and calculate output sizes. + + const bool isVideo = This->isVideo; + + ktx_uint8_t* pXcodedData = prototype->pData; + // Inconveniently, the output buffer size parameter of transcode_image + // has to be in pixels for uncompressed output and in blocks for + // compressed output. The only reason for humouring the API is so + // its buffer size tests provide a real check. An alternative is to + // always provide the size in bytes which will always pass. + ktx_uint32_t outputBlockByteLength + = prototype->_protected->_formatSize.blockSizeInBits / 8; + ktx_size_t xcodedDataLength + = prototype->dataSize / outputBlockByteLength; + ktxLevelIndexEntry* protoLevelIndex; + uint64_t levelOffsetWrite; + const ktxBasisLzEtc1sImageDesc* imageDescs = BGD_ETC1S_IMAGE_DESCS(bgd); + + // Finally we're ready to transcode the slices. + + // FIXME: Iframe flag needs to be queryable by the application. In Basis + // the app can query file_info and image_info from the transcoder which + // returns a structure with lots of info about the image. + + protoLevelIndex = protoPriv._levelIndex; + levelOffsetWrite = 0; + for (int32_t level = This->numLevels - 1; level >= 0; level--) { + uint64_t levelOffset = ktxTexture2_levelDataOffset(This, level); + uint64_t writeOffset = levelOffsetWrite; + uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength; + uint32_t levelWidth = MAX(1, This->baseWidth >> level); + uint32_t levelHeight = MAX(1, This->baseHeight >> level); + // ETC1S texel block dimensions + const uint32_t bw = 4, bh = 4; + uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw; + uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh; + uint32_t depth = MAX(1, This->baseDepth >> level); + //uint32_t faceSlices = This->numFaces == 1 ? depth : This->numFaces; + uint32_t faceSlices = This->numFaces * depth; + uint32_t numImages = This->numLayers * faceSlices; + uint32_t image = firstImages[level]; + uint32_t endImage = image + numImages; + ktx_size_t levelImageSizeOut, levelSizeOut; + uint32_t stateIndex = 0; + + levelSizeOut = 0; + // FIXME: Figure out a way to get the size out of the transcoder. + levelImageSizeOut = ktxTexture2_GetImageSize(prototype, level); + for (; image < endImage; image++) { + const ktxBasisLzEtc1sImageDesc& imageDesc = imageDescs[image]; + + basisu_transcoder_state& xcoderState = xcoderStates[stateIndex]; + // We have face0 [face1 ...] within each layer. Use `stateIndex` + // rather than a double loop of layers and faceSlices as this + // works for 3d texture and non-array cube maps as well as + // cube map arrays without special casing. + if (++stateIndex == xcoderStates.size()) + stateIndex = 0; + + if (alphaContent != eNone) + { + // The slice descriptions should have alpha information. + if (imageDesc.alphaSliceByteOffset == 0 + || imageDesc.alphaSliceByteLength == 0) + return KTX_FILE_DATA_ERROR; + } + + bool status; + status = bit.transcode_image( + (transcoder_texture_format)outputFormat, + pXcodedData + writeOffset, + (uint32_t)(xcodedDataLength - writeOffsetBlocks), + This->pData, + (uint32_t)This->dataSize, + levelBlocksX, + levelBlocksY, + levelWidth, + levelHeight, + level, + (uint32_t)(levelOffset + imageDesc.rgbSliceByteOffset), + imageDesc.rgbSliceByteLength, + (uint32_t)(levelOffset + imageDesc.alphaSliceByteOffset), + imageDesc.alphaSliceByteLength, + transcodeFlags, + alphaContent != eNone, + isVideo, + // Our P-Frame flag is in the same bit as + // cSliceDescFlagsFrameIsIFrame. We have to + // invert it to make it an I-Frame flag. + // + // API currently doesn't have any way to pass + // the I-Frame flag. + //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame, + 0, // output_row_pitch_in_blocks_or_pixels + &xcoderState, + 0 // output_rows_in_pixels + ); + if (!status) { + result = KTX_TRANSCODE_FAILED; + goto cleanup; + } + + writeOffset += levelImageSizeOut; + levelSizeOut += levelImageSizeOut; + } // end images loop + protoLevelIndex[level].byteOffset = levelOffsetWrite; + protoLevelIndex[level].byteLength = levelSizeOut; + protoLevelIndex[level].uncompressedByteLength = levelSizeOut; + levelOffsetWrite += levelSizeOut; + assert(levelOffsetWrite == writeOffset); + // In case of transcoding to uncompressed. + levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment, + levelOffsetWrite); + } // level loop + + result = KTX_SUCCESS; + +cleanup: + delete[] firstImages; + return result; +} + + +KTX_error_code +ktxTexture2_transcodeUastc(ktxTexture2* This, + alpha_content_e alphaContent, + ktxTexture2* prototype, + ktx_transcode_fmt_e outputFormat, + ktx_transcode_flags transcodeFlags) +{ + assert(This->supercompressionScheme != KTX_SS_BASIS_LZ); + + ktx_uint8_t* pXcodedData = prototype->pData; + ktx_uint32_t outputBlockByteLength + = prototype->_protected->_formatSize.blockSizeInBits / 8; + ktx_size_t xcodedDataLength + = prototype->dataSize / outputBlockByteLength; + DECLARE_PRIVATE(protoPriv, prototype); + ktxLevelIndexEntry* protoLevelIndex = protoPriv._levelIndex; + ktx_size_t levelOffsetWrite = 0; + + basisu_lowlevel_uastc_transcoder uit; + // See comment on same declaration in transcodeEtc1s. + std::vector<basisu_transcoder_state> xcoderStates; + xcoderStates.resize(This->isVideo ? This->numFaces : 1); + + for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--) + { + ktx_uint32_t depth; + uint64_t writeOffset = levelOffsetWrite; + uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength; + ktx_size_t levelImageSizeIn, levelImageOffsetIn; + ktx_size_t levelImageSizeOut, levelSizeOut; + ktx_uint32_t levelImageCount; + uint32_t levelWidth = MAX(1, This->baseWidth >> level); + uint32_t levelHeight = MAX(1, This->baseHeight >> level); + // UASTC texel block dimensions + const uint32_t bw = 4, bh = 4; + uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw; + uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh; + uint32_t stateIndex = 0; + + depth = MAX(1, This->baseDepth >> level); + + levelImageCount = This->numLayers * This->numFaces * depth; + levelImageSizeIn = ktxTexture_calcImageSize(ktxTexture(This), level, + KTX_FORMAT_VERSION_TWO); + levelImageSizeOut = ktxTexture_calcImageSize(ktxTexture(prototype), + level, + KTX_FORMAT_VERSION_TWO); + + levelImageOffsetIn = ktxTexture2_levelDataOffset(This, level); + levelSizeOut = 0; + bool status; + for (uint32_t image = 0; image < levelImageCount; image++) { + basisu_transcoder_state& xcoderState = xcoderStates[stateIndex]; + // See comment before same lines in transcodeEtc1s. + if (++stateIndex == xcoderStates.size()) + stateIndex = 0; + + status = uit.transcode_image( + (transcoder_texture_format)outputFormat, + pXcodedData + writeOffset, + (uint32_t)(xcodedDataLength - writeOffsetBlocks), + This->pData, + (uint32_t)This->dataSize, + levelBlocksX, + levelBlocksY, + levelWidth, + levelHeight, + level, + (uint32_t)levelImageOffsetIn, + (uint32_t)levelImageSizeIn, + transcodeFlags, + alphaContent != eNone, + This->isVideo, // is_video + //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame, + 0, // output_row_pitch_in_blocks_or_pixels + &xcoderState, // pState + 0, // output_rows_in_pixels, + -1, // channel0 + -1 // channel1 + ); + if (!status) + return KTX_TRANSCODE_FAILED; + writeOffset += levelImageSizeOut; + levelSizeOut += levelImageSizeOut; + levelImageOffsetIn += levelImageSizeIn; + } + protoLevelIndex[level].byteOffset = levelOffsetWrite; + // writeOffset will be equal to total size of the images in the level. + protoLevelIndex[level].byteLength = levelSizeOut; + protoLevelIndex[level].uncompressedByteLength = levelSizeOut; + levelOffsetWrite += levelSizeOut; + } + // In case of transcoding to uncompressed. + levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment, + levelOffsetWrite); + return KTX_SUCCESS; +} diff --git a/thirdparty/libktx/lib/checkheader.c b/thirdparty/libktx/lib/checkheader.c new file mode 100644 index 0000000000..07e5d919c8 --- /dev/null +++ b/thirdparty/libktx/lib/checkheader.c @@ -0,0 +1,259 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* $Id: ee6f7be4d43390de78e1815ed158012c78ddeff1 $ */ + +/* + * Copyright 2010-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file checkheader.c + * @~English + * + * @brief Function to verify a KTX file header + * + * @author Mark Callow, HI Corporation + */ + +/* + * Author: Georg Kolling, Imagination Technology with modifications + * by Mark Callow, HI Corporation. + */ +#include <assert.h> +#include <string.h> + +#include "ktx.h" +#include "ktxint.h" + +/** + * @internal + * @~English + * @brief Check a KTX file header. + * + * As well as checking that the header identifies a KTX file, the function + * sanity checks the values and returns information about the texture in a + * struct KTX_supplementary_info. + * + * @param pHeader pointer to the KTX header to check + * @param pSuppInfo pointer to a KTX_supplementary_info structure in which to + * return information about the texture. + * + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ + +KTX_error_code ktxCheckHeader1_(KTX_header* pHeader, + KTX_supplemental_info* pSuppInfo) +{ + ktx_uint8_t identifier_reference[12] = KTX_IDENTIFIER_REF; + ktx_uint32_t max_dim; + + assert(pHeader != NULL && pSuppInfo != NULL); + + /* Compare identifier, is this a KTX file? */ + if (memcmp(pHeader->identifier, identifier_reference, 12) != 0) + { + return KTX_UNKNOWN_FILE_FORMAT; + } + + if (pHeader->endianness == KTX_ENDIAN_REF_REV) + { + /* Convert endianness of pHeader fields. */ + _ktxSwapEndian32(&pHeader->glType, 12); + + if (pHeader->glTypeSize != 1 && + pHeader->glTypeSize != 2 && + pHeader->glTypeSize != 4) + { + /* Only 8-, 16-, and 32-bit types supported so far. */ + return KTX_FILE_DATA_ERROR; + } + } + else if (pHeader->endianness != KTX_ENDIAN_REF) + { + return KTX_FILE_DATA_ERROR; + } + + /* Check glType and glFormat */ + pSuppInfo->compressed = 0; + if (pHeader->glType == 0 || pHeader->glFormat == 0) + { + if (pHeader->glType + pHeader->glFormat != 0) + { + /* either both or none of glType, glFormat must be zero */ + return KTX_FILE_DATA_ERROR; + } + pSuppInfo->compressed = 1; + } + + if (pHeader->glFormat == pHeader->glInternalformat) { + // glInternalFormat is either unsized (which is no longer and should + // never have been supported by libktx) or glFormat is sized. + return KTX_FILE_DATA_ERROR; + } + + /* Check texture dimensions. KTX files can store 8 types of textures: + 1D, 2D, 3D, cube, and array variants of these. There is currently + no GL extension for 3D array textures. */ + if ((pHeader->pixelWidth == 0) || + (pHeader->pixelDepth > 0 && pHeader->pixelHeight == 0)) + { + /* texture must have width */ + /* texture must have height if it has depth */ + return KTX_FILE_DATA_ERROR; + } + + + if (pHeader->pixelDepth > 0) + { + if (pHeader->numberOfArrayElements > 0) + { + /* No 3D array textures yet. */ + return KTX_UNSUPPORTED_TEXTURE_TYPE; + } + pSuppInfo->textureDimension = 3; + } + else if (pHeader->pixelHeight > 0) + { + pSuppInfo->textureDimension = 2; + } + else + { + pSuppInfo->textureDimension = 1; + } + + if (pHeader->numberOfFaces == 6) + { + if (pSuppInfo->textureDimension != 2) + { + /* cube map needs 2D faces */ + return KTX_FILE_DATA_ERROR; + } + } + else if (pHeader->numberOfFaces != 1) + { + /* numberOfFaces must be either 1 or 6 */ + return KTX_FILE_DATA_ERROR; + } + + /* Check number of mipmap levels */ + if (pHeader->numberOfMipLevels == 0) + { + pSuppInfo->generateMipmaps = 1; + pHeader->numberOfMipLevels = 1; + } + else + { + pSuppInfo->generateMipmaps = 0; + } + + /* This test works for arrays too because height or depth will be 0. */ + max_dim = MAX(MAX(pHeader->pixelWidth, pHeader->pixelHeight), pHeader->pixelDepth); + if (max_dim < ((ktx_uint32_t)1 << (pHeader->numberOfMipLevels - 1))) + { + /* Can't have more mip levels than 1 + log2(max(width, height, depth)) */ + return KTX_FILE_DATA_ERROR; + } + + return KTX_SUCCESS; +} + +/** + * @internal + * @~English + * @brief Check a KTX2 file header. + * + * As well as checking that the header identifies a KTX 2 file, the function + * sanity checks the values and returns information about the texture in a + * struct KTX_supplementary_info. + * + * @param pHeader pointer to the KTX header to check + * @param pSuppInfo pointer to a KTX_supplementary_info structure in which to + * return information about the texture. + * + * @author Mark Callow, HI Corporation + */ +KTX_error_code ktxCheckHeader2_(KTX_header2* pHeader, + KTX_supplemental_info* pSuppInfo) +{ +// supp info is compressed, generateMipmaps and num dimensions. Don't need +// compressed as formatSize gives us that. I think the other 2 aren't needed. + ktx_uint8_t identifier_reference[12] = KTX2_IDENTIFIER_REF; + + assert(pHeader != NULL && pSuppInfo != NULL); + ktx_uint32_t max_dim; + + /* Compare identifier, is this a KTX file? */ + if (memcmp(pHeader->identifier, identifier_reference, 12) != 0) + { + return KTX_UNKNOWN_FILE_FORMAT; + } + + /* Check texture dimensions. KTX files can store 8 types of textures: + 1D, 2D, 3D, cube, and array variants of these. There is currently + no extension for 3D array textures in any 3D API. */ + if ((pHeader->pixelWidth == 0) || + (pHeader->pixelDepth > 0 && pHeader->pixelHeight == 0)) + { + /* texture must have width */ + /* texture must have height if it has depth */ + return KTX_FILE_DATA_ERROR; + } + + if (pHeader->pixelDepth > 0) + { + if (pHeader->layerCount > 0) + { + /* No 3D array textures yet. */ + return KTX_UNSUPPORTED_TEXTURE_TYPE; + } + pSuppInfo->textureDimension = 3; + } + else if (pHeader->pixelHeight > 0) + { + pSuppInfo->textureDimension = 2; + } + else + { + pSuppInfo->textureDimension = 1; + } + + if (pHeader->faceCount == 6) + { + if (pSuppInfo->textureDimension != 2) + { + /* cube map needs 2D faces */ + return KTX_FILE_DATA_ERROR; + } + } + else if (pHeader->faceCount != 1) + { + /* numberOfFaces must be either 1 or 6 */ + return KTX_FILE_DATA_ERROR; + } + + // Check number of mipmap levels + if (pHeader->levelCount == 0) + { + pSuppInfo->generateMipmaps = 1; + pHeader->levelCount = 1; + } + else + { + pSuppInfo->generateMipmaps = 0; + } + + // This test works for arrays too because height or depth will be 0. + max_dim = MAX(MAX(pHeader->pixelWidth, pHeader->pixelHeight), pHeader->pixelDepth); + if (max_dim < ((ktx_uint32_t)1 << (pHeader->levelCount - 1))) + { + // Can't have more mip levels than 1 + log2(max(width, height, depth)) + return KTX_FILE_DATA_ERROR; + } + + return KTX_SUCCESS; + +} diff --git a/thirdparty/libktx/lib/dfdutils/KHR/khr_df.h b/thirdparty/libktx/lib/dfdutils/KHR/khr_df.h new file mode 100644 index 0000000000..bbd0d14bd9 --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/KHR/khr_df.h @@ -0,0 +1,619 @@ +/* The Khronos Data Format Specification (version 1.3) */ +/* +** Copyright 2015-2020 The Khronos Group Inc. +** SPDX-License-Identifier: Apache-2.0 +*/ + +/* This header defines a structure that can describe the layout of image + formats in memory. This means that the data format is transparent to + the application, and the expectation is that this should be used when + the layout is defined external to the API. Many Khronos APIs deliberately + keep the internal layout of images opaque, to allow proprietary layouts + and optimisations. This structure is not appropriate for describing + opaque layouts. */ + +/* We stick to standard C89 constructs for simplicity and portability. */ + +#ifndef _KHR_DATA_FORMAT_H_ +#define _KHR_DATA_FORMAT_H_ + +/* Accessors */ +typedef enum _khr_word_e { + KHR_DF_WORD_VENDORID = 0U, + KHR_DF_WORD_DESCRIPTORTYPE = 0U, + KHR_DF_WORD_VERSIONNUMBER = 1U, + KHR_DF_WORD_DESCRIPTORBLOCKSIZE = 1U, + KHR_DF_WORD_MODEL = 2U, + KHR_DF_WORD_PRIMARIES = 2U, + KHR_DF_WORD_TRANSFER = 2U, + KHR_DF_WORD_FLAGS = 2U, + KHR_DF_WORD_TEXELBLOCKDIMENSION0 = 3U, + KHR_DF_WORD_TEXELBLOCKDIMENSION1 = 3U, + KHR_DF_WORD_TEXELBLOCKDIMENSION2 = 3U, + KHR_DF_WORD_TEXELBLOCKDIMENSION3 = 3U, + KHR_DF_WORD_BYTESPLANE0 = 4U, + KHR_DF_WORD_BYTESPLANE1 = 4U, + KHR_DF_WORD_BYTESPLANE2 = 4U, + KHR_DF_WORD_BYTESPLANE3 = 4U, + KHR_DF_WORD_BYTESPLANE4 = 5U, + KHR_DF_WORD_BYTESPLANE5 = 5U, + KHR_DF_WORD_BYTESPLANE6 = 5U, + KHR_DF_WORD_BYTESPLANE7 = 5U, + KHR_DF_WORD_SAMPLESTART = 6U, + KHR_DF_WORD_SAMPLEWORDS = 4U +} khr_df_word_e; + +typedef enum _khr_df_shift_e { + KHR_DF_SHIFT_VENDORID = 0U, + KHR_DF_SHIFT_DESCRIPTORTYPE = 17U, + KHR_DF_SHIFT_VERSIONNUMBER = 0U, + KHR_DF_SHIFT_DESCRIPTORBLOCKSIZE = 16U, + KHR_DF_SHIFT_MODEL = 0U, + KHR_DF_SHIFT_PRIMARIES = 8U, + KHR_DF_SHIFT_TRANSFER = 16U, + KHR_DF_SHIFT_FLAGS = 24U, + KHR_DF_SHIFT_TEXELBLOCKDIMENSION0 = 0U, + KHR_DF_SHIFT_TEXELBLOCKDIMENSION1 = 8U, + KHR_DF_SHIFT_TEXELBLOCKDIMENSION2 = 16U, + KHR_DF_SHIFT_TEXELBLOCKDIMENSION3 = 24U, + KHR_DF_SHIFT_BYTESPLANE0 = 0U, + KHR_DF_SHIFT_BYTESPLANE1 = 8U, + KHR_DF_SHIFT_BYTESPLANE2 = 16U, + KHR_DF_SHIFT_BYTESPLANE3 = 24U, + KHR_DF_SHIFT_BYTESPLANE4 = 0U, + KHR_DF_SHIFT_BYTESPLANE5 = 8U, + KHR_DF_SHIFT_BYTESPLANE6 = 16U, + KHR_DF_SHIFT_BYTESPLANE7 = 24U +} khr_df_shift_e; + +typedef enum _khr_df_mask_e { + KHR_DF_MASK_VENDORID = 0x1FFFFU, + KHR_DF_MASK_DESCRIPTORTYPE = 0x7FFFU, + KHR_DF_MASK_VERSIONNUMBER = 0xFFFFU, + KHR_DF_MASK_DESCRIPTORBLOCKSIZE = 0xFFFFU, + KHR_DF_MASK_MODEL = 0xFFU, + KHR_DF_MASK_PRIMARIES = 0xFFU, + KHR_DF_MASK_TRANSFER = 0xFFU, + KHR_DF_MASK_FLAGS = 0xFFU, + KHR_DF_MASK_TEXELBLOCKDIMENSION0 = 0xFFU, + KHR_DF_MASK_TEXELBLOCKDIMENSION1 = 0xFFU, + KHR_DF_MASK_TEXELBLOCKDIMENSION2 = 0xFFU, + KHR_DF_MASK_TEXELBLOCKDIMENSION3 = 0xFFU, + KHR_DF_MASK_BYTESPLANE0 = 0xFFU, + KHR_DF_MASK_BYTESPLANE1 = 0xFFU, + KHR_DF_MASK_BYTESPLANE2 = 0xFFU, + KHR_DF_MASK_BYTESPLANE3 = 0xFFU, + KHR_DF_MASK_BYTESPLANE4 = 0xFFU, + KHR_DF_MASK_BYTESPLANE5 = 0xFFU, + KHR_DF_MASK_BYTESPLANE6 = 0xFFU, + KHR_DF_MASK_BYTESPLANE7 = 0xFFU +} khr_df_mask_e; + +/* Helper macro: + Extract field X from basic descriptor block BDB */ +#define KHR_DFDVAL(BDB, X) \ + (((BDB)[KHR_DF_WORD_ ## X] >> (KHR_DF_SHIFT_ ## X)) \ + & (KHR_DF_MASK_ ## X)) + +/* Helper macro: + Set field X of basic descriptor block BDB */ +#define KHR_DFDSETVAL(BDB, X, val) \ + ((BDB)[KHR_DF_WORD_ ## X] = \ + ((BDB)[KHR_DF_WORD_ ## X] & \ + ~((KHR_DF_MASK_ ## X) << (KHR_DF_SHIFT_ ## X))) | \ + (((val) & (KHR_DF_MASK_ ## X)) << (KHR_DF_SHIFT_ ## X))) + +/* Offsets relative to the start of a sample */ +typedef enum _khr_df_sampleword_e { + KHR_DF_SAMPLEWORD_BITOFFSET = 0U, + KHR_DF_SAMPLEWORD_BITLENGTH = 0U, + KHR_DF_SAMPLEWORD_CHANNELID = 0U, + KHR_DF_SAMPLEWORD_QUALIFIERS = 0U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION0 = 1U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION1 = 1U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION2 = 1U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION3 = 1U, + KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL = 1U, + KHR_DF_SAMPLEWORD_SAMPLELOWER = 2U, + KHR_DF_SAMPLEWORD_SAMPLEUPPER = 3U +} khr_df_sampleword_e; + +typedef enum _khr_df_sampleshift_e { + KHR_DF_SAMPLESHIFT_BITOFFSET = 0U, + KHR_DF_SAMPLESHIFT_BITLENGTH = 16U, + KHR_DF_SAMPLESHIFT_CHANNELID = 24U, + /* N.B. Qualifiers are defined as an offset into a byte */ + KHR_DF_SAMPLESHIFT_QUALIFIERS = 24U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION0 = 0U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION1 = 8U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION2 = 16U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION3 = 24U, + KHR_DF_SAMPLESHIFT_SAMPLEPOSITION_ALL = 0U, + KHR_DF_SAMPLESHIFT_SAMPLELOWER = 0U, + KHR_DF_SAMPLESHIFT_SAMPLEUPPER = 0U +} khr_df_sampleshift_e; + +typedef enum _khr_df_samplemask_e { + KHR_DF_SAMPLEMASK_BITOFFSET = 0xFFFFU, + KHR_DF_SAMPLEMASK_BITLENGTH = 0xFF, + KHR_DF_SAMPLEMASK_CHANNELID = 0xF, + /* N.B. Qualifiers are defined as an offset into a byte */ + KHR_DF_SAMPLEMASK_QUALIFIERS = 0xF0, + KHR_DF_SAMPLEMASK_SAMPLEPOSITION0 = 0xFF, + KHR_DF_SAMPLEMASK_SAMPLEPOSITION1 = 0xFF, + KHR_DF_SAMPLEMASK_SAMPLEPOSITION2 = 0xFF, + KHR_DF_SAMPLEMASK_SAMPLEPOSITION3 = 0xFF, + /* ISO C restricts enum values to range of int hence the + cast. We do it verbosely instead of using -1 to ensure + it is a 32-bit value even if int is 64 bits. */ + KHR_DF_SAMPLEMASK_SAMPLEPOSITION_ALL = (int) 0xFFFFFFFFU, + KHR_DF_SAMPLEMASK_SAMPLELOWER = (int) 0xFFFFFFFFU, + KHR_DF_SAMPLEMASK_SAMPLEUPPER = (int) 0xFFFFFFFFU +} khr_df_samplemask_e; + +/* Helper macro: + Extract field X of sample S from basic descriptor block BDB */ +#define KHR_DFDSVAL(BDB, S, X) \ + (((BDB)[KHR_DF_WORD_SAMPLESTART + \ + ((S) * KHR_DF_WORD_SAMPLEWORDS) + \ + KHR_DF_SAMPLEWORD_ ## X] >> (KHR_DF_SAMPLESHIFT_ ## X)) \ + & (KHR_DF_SAMPLEMASK_ ## X)) + +/* Helper macro: + Set field X of sample S of basic descriptor block BDB */ +#define KHR_DFDSETSVAL(BDB, S, X, val) \ + ((BDB)[KHR_DF_WORD_SAMPLESTART + \ + ((S) * KHR_DF_WORD_SAMPLEWORDS) + \ + KHR_DF_SAMPLEWORD_ ## X] = \ + ((BDB)[KHR_DF_WORD_SAMPLESTART + \ + ((S) * KHR_DF_WORD_SAMPLEWORDS) + \ + KHR_DF_SAMPLEWORD_ ## X] & \ + ~((uint32_t)(KHR_DF_SAMPLEMASK_ ## X) << (KHR_DF_SAMPLESHIFT_ ## X))) | \ + (((val) & (uint32_t)(KHR_DF_SAMPLEMASK_ ## X)) << (KHR_DF_SAMPLESHIFT_ ## X))) + +/* Helper macro: + Number of samples in basic descriptor block BDB */ +#define KHR_DFDSAMPLECOUNT(BDB) \ + (((KHR_DFDVAL(BDB, DESCRIPTORBLOCKSIZE) >> 2) - \ + KHR_DF_WORD_SAMPLESTART) \ + / KHR_DF_WORD_SAMPLEWORDS) + +/* Helper macro: + Size in words of basic descriptor block for S samples */ +#define KHR_DFDSIZEWORDS(S) \ + (KHR_DF_WORD_SAMPLESTART + \ + (S) * KHR_DF_WORD_SAMPLEWORDS) + +/* Vendor ids */ +typedef enum _khr_df_vendorid_e { + /* Standard Khronos descriptor */ + KHR_DF_VENDORID_KHRONOS = 0U, + KHR_DF_VENDORID_MAX = 0x1FFFFU +} khr_df_vendorid_e; + +/* Descriptor types */ +typedef enum _khr_df_khr_descriptortype_e { + /* Default Khronos basic descriptor block */ + KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT = 0U, + /* Extension descriptor block for additional planes */ + KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_PLANES = 0x6001U, + /* Extension descriptor block for additional dimensions */ + KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_DIMENSIONS = 0x6002U, + /* Bit indicates modifying requires understanding this extension */ + KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_WRITE_BIT = 0x2000U, + /* Bit indicates processing requires understanding this extension */ + KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_DECODE_BIT = 0x4000U, + KHR_DF_KHR_DESCRIPTORTYPE_MAX = 0x7FFFU +} khr_df_khr_descriptortype_e; + +/* Descriptor block version */ +typedef enum _khr_df_versionnumber_e { + /* Standard Khronos descriptor */ + KHR_DF_VERSIONNUMBER_1_0 = 0U, /* Version 1.0 of the specification */ + KHR_DF_VERSIONNUMBER_1_1 = 0U, /* Version 1.1 did not bump the version number */ + KHR_DF_VERSIONNUMBER_1_2 = 1U, /* Version 1.2 increased the version number */ + KHR_DF_VERSIONNUMBER_1_3 = 2U, /* Version 1.3 increased the version number */ + KHR_DF_VERSIONNUMBER_LATEST = KHR_DF_VERSIONNUMBER_1_3, + KHR_DF_VERSIONNUMBER_MAX = 0xFFFFU +} khr_df_versionnumber_e; + +/* Model in which the color coordinate space is defined. + There is no requirement that a color format use all the + channel types that are defined in the color model. */ +typedef enum _khr_df_model_e { + /* No interpretation of color channels defined */ + KHR_DF_MODEL_UNSPECIFIED = 0U, + /* Color primaries (red, green, blue) + alpha, depth and stencil */ + KHR_DF_MODEL_RGBSDA = 1U, + /* Color differences (Y', Cb, Cr) + alpha, depth and stencil */ + KHR_DF_MODEL_YUVSDA = 2U, + /* Color differences (Y', I, Q) + alpha, depth and stencil */ + KHR_DF_MODEL_YIQSDA = 3U, + /* Perceptual color (CIE L*a*b*) + alpha, depth and stencil */ + KHR_DF_MODEL_LABSDA = 4U, + /* Subtractive colors (cyan, magenta, yellow, black) + alpha */ + KHR_DF_MODEL_CMYKA = 5U, + /* Non-color coordinate data (X, Y, Z, W) */ + KHR_DF_MODEL_XYZW = 6U, + /* Hue, saturation, value, hue angle on color circle, plus alpha */ + KHR_DF_MODEL_HSVA_ANG = 7U, + /* Hue, saturation, lightness, hue angle on color circle, plus alpha */ + KHR_DF_MODEL_HSLA_ANG = 8U, + /* Hue, saturation, value, hue on color hexagon, plus alpha */ + KHR_DF_MODEL_HSVA_HEX = 9U, + /* Hue, saturation, lightness, hue on color hexagon, plus alpha */ + KHR_DF_MODEL_HSLA_HEX = 10U, + /* Lightweight approximate color difference (luma, orange, green) */ + KHR_DF_MODEL_YCGCOA = 11U, + /* ITU BT.2020 constant luminance YcCbcCrc */ + KHR_DF_MODEL_YCCBCCRC = 12U, + /* ITU BT.2100 constant intensity ICtCp */ + KHR_DF_MODEL_ICTCP = 13U, + /* CIE 1931 XYZ color coordinates (X, Y, Z) */ + KHR_DF_MODEL_CIEXYZ = 14U, + /* CIE 1931 xyY color coordinates (X, Y, Y) */ + KHR_DF_MODEL_CIEXYY = 15U, + + /* Compressed formats start at 128. */ + /* These compressed formats should generally have a single sample, + sited at the 0,0 position of the texel block. Where multiple + channels are used to distinguish formats, these should be cosited. */ + /* Direct3D (and S3) compressed formats */ + /* Note that premultiplied status is recorded separately */ + /* DXT1 "channels" are RGB (0), Alpha (1) */ + /* DXT1/BC1 with one channel is opaque */ + /* DXT1/BC1 with a cosited alpha sample is transparent */ + KHR_DF_MODEL_DXT1A = 128U, + KHR_DF_MODEL_BC1A = 128U, + /* DXT2/DXT3/BC2, with explicit 4-bit alpha */ + KHR_DF_MODEL_DXT2 = 129U, + KHR_DF_MODEL_DXT3 = 129U, + KHR_DF_MODEL_BC2 = 129U, + /* DXT4/DXT5/BC3, with interpolated alpha */ + KHR_DF_MODEL_DXT4 = 130U, + KHR_DF_MODEL_DXT5 = 130U, + KHR_DF_MODEL_BC3 = 130U, + /* BC4 - single channel interpolated 8-bit data */ + /* (The UNORM/SNORM variation is recorded in the channel data) */ + KHR_DF_MODEL_BC4 = 131U, + /* BC5 - two channel interpolated 8-bit data */ + /* (The UNORM/SNORM variation is recorded in the channel data) */ + KHR_DF_MODEL_BC5 = 132U, + /* BC6H - DX11 format for 16-bit float channels */ + KHR_DF_MODEL_BC6H = 133U, + /* BC7 - DX11 format */ + KHR_DF_MODEL_BC7 = 134U, + /* Gap left for future desktop expansion */ + + /* Mobile compressed formats follow */ + /* A format of ETC1 indicates that the format shall be decodable + by an ETC1-compliant decoder and not rely on ETC2 features */ + KHR_DF_MODEL_ETC1 = 160U, + /* A format of ETC2 is permitted to use ETC2 encodings on top of + the baseline ETC1 specification */ + /* The ETC2 format has channels "red", "green", "RGB" and "alpha", + which should be cosited samples */ + /* Punch-through alpha can be distinguished from full alpha by + the plane size in bytes required for the texel block */ + KHR_DF_MODEL_ETC2 = 161U, + /* Adaptive Scalable Texture Compression */ + /* ASTC HDR vs LDR is determined by the float flag in the channel */ + /* ASTC block size can be distinguished by texel block size */ + KHR_DF_MODEL_ASTC = 162U, + /* ETC1S is a simplified subset of ETC1 */ + KHR_DF_MODEL_ETC1S = 163U, + /* PowerVR Texture Compression */ + KHR_DF_MODEL_PVRTC = 164U, + KHR_DF_MODEL_PVRTC2 = 165U, + KHR_DF_MODEL_UASTC = 166U, + /* Proprietary formats (ATITC, etc.) should follow */ + KHR_DF_MODEL_MAX = 0xFFU +} khr_df_model_e; + +/* Definition of channel names for each color model */ +typedef enum _khr_df_model_channels_e { + /* Unspecified format with nominal channel numbering */ + KHR_DF_CHANNEL_UNSPECIFIED_0 = 0U, + KHR_DF_CHANNEL_UNSPECIFIED_1 = 1U, + KHR_DF_CHANNEL_UNSPECIFIED_2 = 2U, + KHR_DF_CHANNEL_UNSPECIFIED_3 = 3U, + KHR_DF_CHANNEL_UNSPECIFIED_4 = 4U, + KHR_DF_CHANNEL_UNSPECIFIED_5 = 5U, + KHR_DF_CHANNEL_UNSPECIFIED_6 = 6U, + KHR_DF_CHANNEL_UNSPECIFIED_7 = 7U, + KHR_DF_CHANNEL_UNSPECIFIED_8 = 8U, + KHR_DF_CHANNEL_UNSPECIFIED_9 = 9U, + KHR_DF_CHANNEL_UNSPECIFIED_10 = 10U, + KHR_DF_CHANNEL_UNSPECIFIED_11 = 11U, + KHR_DF_CHANNEL_UNSPECIFIED_12 = 12U, + KHR_DF_CHANNEL_UNSPECIFIED_13 = 13U, + KHR_DF_CHANNEL_UNSPECIFIED_14 = 14U, + KHR_DF_CHANNEL_UNSPECIFIED_15 = 15U, + /* MODEL_RGBSDA - red, green, blue, stencil, depth, alpha */ + KHR_DF_CHANNEL_RGBSDA_RED = 0U, + KHR_DF_CHANNEL_RGBSDA_R = 0U, + KHR_DF_CHANNEL_RGBSDA_GREEN = 1U, + KHR_DF_CHANNEL_RGBSDA_G = 1U, + KHR_DF_CHANNEL_RGBSDA_BLUE = 2U, + KHR_DF_CHANNEL_RGBSDA_B = 2U, + KHR_DF_CHANNEL_RGBSDA_STENCIL = 13U, + KHR_DF_CHANNEL_RGBSDA_S = 13U, + KHR_DF_CHANNEL_RGBSDA_DEPTH = 14U, + KHR_DF_CHANNEL_RGBSDA_D = 14U, + KHR_DF_CHANNEL_RGBSDA_ALPHA = 15U, + KHR_DF_CHANNEL_RGBSDA_A = 15U, + /* MODEL_YUVSDA - luma, Cb, Cr, stencil, depth, alpha */ + KHR_DF_CHANNEL_YUVSDA_Y = 0U, + KHR_DF_CHANNEL_YUVSDA_CB = 1U, + KHR_DF_CHANNEL_YUVSDA_U = 1U, + KHR_DF_CHANNEL_YUVSDA_CR = 2U, + KHR_DF_CHANNEL_YUVSDA_V = 2U, + KHR_DF_CHANNEL_YUVSDA_STENCIL = 13U, + KHR_DF_CHANNEL_YUVSDA_S = 13U, + KHR_DF_CHANNEL_YUVSDA_DEPTH = 14U, + KHR_DF_CHANNEL_YUVSDA_D = 14U, + KHR_DF_CHANNEL_YUVSDA_ALPHA = 15U, + KHR_DF_CHANNEL_YUVSDA_A = 15U, + /* MODEL_YIQSDA - luma, in-phase, quadrature, stencil, depth, alpha */ + KHR_DF_CHANNEL_YIQSDA_Y = 0U, + KHR_DF_CHANNEL_YIQSDA_I = 1U, + KHR_DF_CHANNEL_YIQSDA_Q = 2U, + KHR_DF_CHANNEL_YIQSDA_STENCIL = 13U, + KHR_DF_CHANNEL_YIQSDA_S = 13U, + KHR_DF_CHANNEL_YIQSDA_DEPTH = 14U, + KHR_DF_CHANNEL_YIQSDA_D = 14U, + KHR_DF_CHANNEL_YIQSDA_ALPHA = 15U, + KHR_DF_CHANNEL_YIQSDA_A = 15U, + /* MODEL_LABSDA - CIELAB/L*a*b* luma, red-green, blue-yellow, stencil, depth, alpha */ + KHR_DF_CHANNEL_LABSDA_L = 0U, + KHR_DF_CHANNEL_LABSDA_A = 1U, + KHR_DF_CHANNEL_LABSDA_B = 2U, + KHR_DF_CHANNEL_LABSDA_STENCIL = 13U, + KHR_DF_CHANNEL_LABSDA_S = 13U, + KHR_DF_CHANNEL_LABSDA_DEPTH = 14U, + KHR_DF_CHANNEL_LABSDA_D = 14U, + KHR_DF_CHANNEL_LABSDA_ALPHA = 15U, + /* NOTE: KHR_DF_CHANNEL_LABSDA_A is not a synonym for alpha! */ + /* MODEL_CMYKA - cyan, magenta, yellow, key/blacK, alpha */ + KHR_DF_CHANNEL_CMYKSDA_CYAN = 0U, + KHR_DF_CHANNEL_CMYKSDA_C = 0U, + KHR_DF_CHANNEL_CMYKSDA_MAGENTA = 1U, + KHR_DF_CHANNEL_CMYKSDA_M = 1U, + KHR_DF_CHANNEL_CMYKSDA_YELLOW = 2U, + KHR_DF_CHANNEL_CMYKSDA_Y = 2U, + KHR_DF_CHANNEL_CMYKSDA_KEY = 3U, + KHR_DF_CHANNEL_CMYKSDA_BLACK = 3U, + KHR_DF_CHANNEL_CMYKSDA_K = 3U, + KHR_DF_CHANNEL_CMYKSDA_ALPHA = 15U, + KHR_DF_CHANNEL_CMYKSDA_A = 15U, + /* MODEL_XYZW - coordinates x, y, z, w */ + KHR_DF_CHANNEL_XYZW_X = 0U, + KHR_DF_CHANNEL_XYZW_Y = 1U, + KHR_DF_CHANNEL_XYZW_Z = 2U, + KHR_DF_CHANNEL_XYZW_W = 3U, + /* MODEL_HSVA_ANG - value (luma), saturation, hue, alpha, angular projection, conical space */ + KHR_DF_CHANNEL_HSVA_ANG_VALUE = 0U, + KHR_DF_CHANNEL_HSVA_ANG_V = 0U, + KHR_DF_CHANNEL_HSVA_ANG_SATURATION = 1U, + KHR_DF_CHANNEL_HSVA_ANG_S = 1U, + KHR_DF_CHANNEL_HSVA_ANG_HUE = 2U, + KHR_DF_CHANNEL_HSVA_ANG_H = 2U, + KHR_DF_CHANNEL_HSVA_ANG_ALPHA = 15U, + KHR_DF_CHANNEL_HSVA_ANG_A = 15U, + /* MODEL_HSLA_ANG - lightness (luma), saturation, hue, alpha, angular projection, double conical space */ + KHR_DF_CHANNEL_HSLA_ANG_LIGHTNESS = 0U, + KHR_DF_CHANNEL_HSLA_ANG_L = 0U, + KHR_DF_CHANNEL_HSLA_ANG_SATURATION = 1U, + KHR_DF_CHANNEL_HSLA_ANG_S = 1U, + KHR_DF_CHANNEL_HSLA_ANG_HUE = 2U, + KHR_DF_CHANNEL_HSLA_ANG_H = 2U, + KHR_DF_CHANNEL_HSLA_ANG_ALPHA = 15U, + KHR_DF_CHANNEL_HSLA_ANG_A = 15U, + /* MODEL_HSVA_HEX - value (luma), saturation, hue, alpha, hexagonal projection, conical space */ + KHR_DF_CHANNEL_HSVA_HEX_VALUE = 0U, + KHR_DF_CHANNEL_HSVA_HEX_V = 0U, + KHR_DF_CHANNEL_HSVA_HEX_SATURATION = 1U, + KHR_DF_CHANNEL_HSVA_HEX_S = 1U, + KHR_DF_CHANNEL_HSVA_HEX_HUE = 2U, + KHR_DF_CHANNEL_HSVA_HEX_H = 2U, + KHR_DF_CHANNEL_HSVA_HEX_ALPHA = 15U, + KHR_DF_CHANNEL_HSVA_HEX_A = 15U, + /* MODEL_HSLA_HEX - lightness (luma), saturation, hue, alpha, hexagonal projection, double conical space */ + KHR_DF_CHANNEL_HSLA_HEX_LIGHTNESS = 0U, + KHR_DF_CHANNEL_HSLA_HEX_L = 0U, + KHR_DF_CHANNEL_HSLA_HEX_SATURATION = 1U, + KHR_DF_CHANNEL_HSLA_HEX_S = 1U, + KHR_DF_CHANNEL_HSLA_HEX_HUE = 2U, + KHR_DF_CHANNEL_HSLA_HEX_H = 2U, + KHR_DF_CHANNEL_HSLA_HEX_ALPHA = 15U, + KHR_DF_CHANNEL_HSLA_HEX_A = 15U, + /* MODEL_YCGCOA - luma, green delta, orange delta, alpha */ + KHR_DF_CHANNEL_YCGCOA_Y = 0U, + KHR_DF_CHANNEL_YCGCOA_CG = 1U, + KHR_DF_CHANNEL_YCGCOA_CO = 2U, + KHR_DF_CHANNEL_YCGCOA_ALPHA = 15U, + KHR_DF_CHANNEL_YCGCOA_A = 15U, + /* MODEL_CIEXYZ - CIE 1931 X, Y, Z */ + KHR_DF_CHANNEL_CIEXYZ_X = 0U, + KHR_DF_CHANNEL_CIEXYZ_Y = 1U, + KHR_DF_CHANNEL_CIEXYZ_Z = 2U, + /* MODEL_CIEXYY - CIE 1931 x, y, Y */ + KHR_DF_CHANNEL_CIEXYY_X = 0U, + KHR_DF_CHANNEL_CIEXYY_YCHROMA = 1U, + KHR_DF_CHANNEL_CIEXYY_YLUMA = 2U, + + /* Compressed formats */ + /* MODEL_DXT1A/MODEL_BC1A */ + KHR_DF_CHANNEL_DXT1A_COLOR = 0U, + KHR_DF_CHANNEL_BC1A_COLOR = 0U, + KHR_DF_CHANNEL_DXT1A_ALPHAPRESENT = 1U, + KHR_DF_CHANNEL_DXT1A_ALPHA = 1U, + KHR_DF_CHANNEL_BC1A_ALPHAPRESENT = 1U, + KHR_DF_CHANNEL_BC1A_ALPHA = 1U, + /* MODEL_DXT2/3/MODEL_BC2 */ + KHR_DF_CHANNEL_DXT2_COLOR = 0U, + KHR_DF_CHANNEL_DXT3_COLOR = 0U, + KHR_DF_CHANNEL_BC2_COLOR = 0U, + KHR_DF_CHANNEL_DXT2_ALPHA = 15U, + KHR_DF_CHANNEL_DXT3_ALPHA = 15U, + KHR_DF_CHANNEL_BC2_ALPHA = 15U, + /* MODEL_DXT4/5/MODEL_BC3 */ + KHR_DF_CHANNEL_DXT4_COLOR = 0U, + KHR_DF_CHANNEL_DXT5_COLOR = 0U, + KHR_DF_CHANNEL_BC3_COLOR = 0U, + KHR_DF_CHANNEL_DXT4_ALPHA = 15U, + KHR_DF_CHANNEL_DXT5_ALPHA = 15U, + KHR_DF_CHANNEL_BC3_ALPHA = 15U, + /* MODEL_BC4 */ + KHR_DF_CHANNEL_BC4_DATA = 0U, + /* MODEL_BC5 */ + KHR_DF_CHANNEL_BC5_RED = 0U, + KHR_DF_CHANNEL_BC5_R = 0U, + KHR_DF_CHANNEL_BC5_GREEN = 1U, + KHR_DF_CHANNEL_BC5_G = 1U, + /* MODEL_BC6H */ + KHR_DF_CHANNEL_BC6H_COLOR = 0U, + KHR_DF_CHANNEL_BC6H_DATA = 0U, + /* MODEL_BC7 */ + KHR_DF_CHANNEL_BC7_DATA = 0U, + KHR_DF_CHANNEL_BC7_COLOR = 0U, + /* MODEL_ETC1 */ + KHR_DF_CHANNEL_ETC1_DATA = 0U, + KHR_DF_CHANNEL_ETC1_COLOR = 0U, + /* MODEL_ETC2 */ + KHR_DF_CHANNEL_ETC2_RED = 0U, + KHR_DF_CHANNEL_ETC2_R = 0U, + KHR_DF_CHANNEL_ETC2_GREEN = 1U, + KHR_DF_CHANNEL_ETC2_G = 1U, + KHR_DF_CHANNEL_ETC2_COLOR = 2U, + KHR_DF_CHANNEL_ETC2_ALPHA = 15U, + KHR_DF_CHANNEL_ETC2_A = 15U, + /* MODEL_ASTC */ + KHR_DF_CHANNEL_ASTC_DATA = 0U, + /* MODEL_ETC1S */ + KHR_DF_CHANNEL_ETC1S_RGB = 0U, + KHR_DF_CHANNEL_ETC1S_RRR = 3U, + KHR_DF_CHANNEL_ETC1S_GGG = 4U, + KHR_DF_CHANNEL_ETC1S_AAA = 15U, + /* MODEL_PVRTC */ + KHR_DF_CHANNEL_PVRTC_DATA = 0U, + KHR_DF_CHANNEL_PVRTC_COLOR = 0U, + /* MODEL_PVRTC2 */ + KHR_DF_CHANNEL_PVRTC2_DATA = 0U, + KHR_DF_CHANNEL_PVRTC2_COLOR = 0U, + /* MODEL UASTC */ + KHR_DF_CHANNEL_UASTC_DATA = 0U, + KHR_DF_CHANNEL_UASTC_RGB = 0U, + KHR_DF_CHANNEL_UASTC_RGBA = 3U, + KHR_DF_CHANNEL_UASTC_RRR = 4U, + KHR_DF_CHANNEL_UASTC_RRRG = 5U, + KHR_DF_CHANNEL_UASTC_RG = 6U, + + /* Common channel names shared by multiple formats */ + KHR_DF_CHANNEL_COMMON_LUMA = 0U, + KHR_DF_CHANNEL_COMMON_L = 0U, + KHR_DF_CHANNEL_COMMON_STENCIL = 13U, + KHR_DF_CHANNEL_COMMON_S = 13U, + KHR_DF_CHANNEL_COMMON_DEPTH = 14U, + KHR_DF_CHANNEL_COMMON_D = 14U, + KHR_DF_CHANNEL_COMMON_ALPHA = 15U, + KHR_DF_CHANNEL_COMMON_A = 15U +} khr_df_model_channels_e; + +/* Definition of the primary colors in color coordinates. + This is implicitly responsible for defining the conversion + between RGB an YUV color spaces. + LAB and related absolute color models should use + KHR_DF_PRIMARIES_CIEXYZ. */ +typedef enum _khr_df_primaries_e { + /* No color primaries defined */ + KHR_DF_PRIMARIES_UNSPECIFIED = 0U, + /* Color primaries of ITU-R BT.709 and sRGB */ + KHR_DF_PRIMARIES_BT709 = 1U, + /* Synonym for KHR_DF_PRIMARIES_BT709 */ + KHR_DF_PRIMARIES_SRGB = 1U, + /* Color primaries of ITU-R BT.601 (625-line EBU variant) */ + KHR_DF_PRIMARIES_BT601_EBU = 2U, + /* Color primaries of ITU-R BT.601 (525-line SMPTE C variant) */ + KHR_DF_PRIMARIES_BT601_SMPTE = 3U, + /* Color primaries of ITU-R BT.2020 */ + KHR_DF_PRIMARIES_BT2020 = 4U, + /* CIE theoretical color coordinate space */ + KHR_DF_PRIMARIES_CIEXYZ = 5U, + /* Academy Color Encoding System primaries */ + KHR_DF_PRIMARIES_ACES = 6U, + /* Color primaries of ACEScc */ + KHR_DF_PRIMARIES_ACESCC = 7U, + /* Legacy NTSC 1953 primaries */ + KHR_DF_PRIMARIES_NTSC1953 = 8U, + /* Legacy PAL 525-line primaries */ + KHR_DF_PRIMARIES_PAL525 = 9U, + /* Color primaries of Display P3 */ + KHR_DF_PRIMARIES_DISPLAYP3 = 10U, + /* Color primaries of Adobe RGB (1998) */ + KHR_DF_PRIMARIES_ADOBERGB = 11U, + KHR_DF_PRIMARIES_MAX = 0xFFU +} khr_df_primaries_e; + +/* Definition of the optical to digital transfer function + ("gamma correction"). Most transfer functions are not a pure + power function and also include a linear element. + LAB and related absolute color representations should use + KHR_DF_TRANSFER_UNSPECIFIED. */ +typedef enum _khr_df_transfer_e { + /* No transfer function defined */ + KHR_DF_TRANSFER_UNSPECIFIED = 0U, + /* Linear transfer function (value proportional to intensity) */ + KHR_DF_TRANSFER_LINEAR = 1U, + /* Perceptually-linear transfer function of sRGH (~2.4) */ + KHR_DF_TRANSFER_SRGB = 2U, + /* Perceptually-linear transfer function of ITU BT.601, BT.709 and BT.2020 (~1/.45) */ + KHR_DF_TRANSFER_ITU = 3U, + /* SMTPE170M (digital NTSC) defines an alias for the ITU transfer function (~1/.45) */ + KHR_DF_TRANSFER_SMTPE170M = 3U, + /* Perceptually-linear gamma function of original NTSC (simple 2.2 gamma) */ + KHR_DF_TRANSFER_NTSC = 4U, + /* Sony S-log used by Sony video cameras */ + KHR_DF_TRANSFER_SLOG = 5U, + /* Sony S-log 2 used by Sony video cameras */ + KHR_DF_TRANSFER_SLOG2 = 6U, + /* ITU BT.1886 EOTF */ + KHR_DF_TRANSFER_BT1886 = 7U, + /* ITU BT.2100 HLG OETF */ + KHR_DF_TRANSFER_HLG_OETF = 8U, + /* ITU BT.2100 HLG EOTF */ + KHR_DF_TRANSFER_HLG_EOTF = 9U, + /* ITU BT.2100 PQ EOTF */ + KHR_DF_TRANSFER_PQ_EOTF = 10U, + /* ITU BT.2100 PQ OETF */ + KHR_DF_TRANSFER_PQ_OETF = 11U, + /* DCI P3 transfer function */ + KHR_DF_TRANSFER_DCIP3 = 12U, + /* Legacy PAL OETF */ + KHR_DF_TRANSFER_PAL_OETF = 13U, + /* Legacy PAL 625-line EOTF */ + KHR_DF_TRANSFER_PAL625_EOTF = 14U, + /* Legacy ST240 transfer function */ + KHR_DF_TRANSFER_ST240 = 15U, + /* ACEScc transfer function */ + KHR_DF_TRANSFER_ACESCC = 16U, + /* ACEScct transfer function */ + KHR_DF_TRANSFER_ACESCCT = 17U, + /* Adobe RGB (1998) transfer function */ + KHR_DF_TRANSFER_ADOBERGB = 18U, + KHR_DF_TRANSFER_MAX = 0xFFU +} khr_df_transfer_e; + +typedef enum _khr_df_flags_e { + KHR_DF_FLAG_ALPHA_STRAIGHT = 0U, + KHR_DF_FLAG_ALPHA_PREMULTIPLIED = 1U +} khr_df_flags_e; + +typedef enum _khr_df_sample_datatype_qualifiers_e { + KHR_DF_SAMPLE_DATATYPE_LINEAR = 1U << 4U, + KHR_DF_SAMPLE_DATATYPE_EXPONENT = 1U << 5U, + KHR_DF_SAMPLE_DATATYPE_SIGNED = 1U << 6U, + KHR_DF_SAMPLE_DATATYPE_FLOAT = 1U << 7U +} khr_df_sample_datatype_qualifiers_e; + +#endif diff --git a/thirdparty/libktx/lib/dfdutils/colourspaces.c b/thirdparty/libktx/lib/dfdutils/colourspaces.c new file mode 100644 index 0000000000..0d998a71aa --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/colourspaces.c @@ -0,0 +1,51 @@ +/* Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * @brief Helper functions for colourspaces. + */ + +#include <KHR/khr_df.h> +#include "dfd.h" + +typedef struct s_PrimaryMapping { + khr_df_primaries_e dfPrimaryEnum; + Primaries primaries; +} sPrimaryMapping; + +sPrimaryMapping primaryMap[] = { + { KHR_DF_PRIMARIES_BT709, { 0.640f,0.330f, 0.300f,0.600f, 0.150f,0.060f, 0.3127f,0.3290f}}, + { KHR_DF_PRIMARIES_BT601_EBU, { 0.640f,0.330f, 0.290f,0.600f, 0.150f,0.060f, 0.3127f,0.3290f}}, + { KHR_DF_PRIMARIES_BT601_SMPTE, { 0.630f,0.340f, 0.310f,0.595f, 0.155f,0.070f, 0.3127f,0.3290f}}, + { KHR_DF_PRIMARIES_BT2020, { 0.708f,0.292f, 0.170f,0.797f, 0.131f,0.046f, 0.3127f,0.3290f}}, + { KHR_DF_PRIMARIES_CIEXYZ, { 1.0f,0.0f, 0.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f}}, + { KHR_DF_PRIMARIES_ACES, { 0.7347f,0.2653f, 0.0f,1.0f, 0.0001f,-0.077f, 0.32168f,0.33767f}}, + { KHR_DF_PRIMARIES_ACESCC, { 0.713f,0.293f, 0.165f,0.830f, 0.128f,0.044f, 0.32168f,0.33767f}}, + { KHR_DF_PRIMARIES_NTSC1953, { 0.67f,0.33f, 0.21f,0.71f, 0.14f,0.08f, 0.310f,0.316f}}, + { KHR_DF_PRIMARIES_PAL525, { 0.630f,0.340f, 0.310f,0.595f, 0.155f,0.070f, 0.3101f,0.3162f}}, + { KHR_DF_PRIMARIES_DISPLAYP3, { 0.6800f,0.3200f, 0.2650f,0.69f, 0.1500f,0.0600f, 0.3127f,0.3290f}}, + { KHR_DF_PRIMARIES_ADOBERGB, { 0.6400f,0.3300f, 0.2100f,0.71f, 0.1500f,0.0600f, 0.3127f,0.3290f}}}; + +/** + * @brief Map a set of primaries to a KDFS primaries enum. + * + * @param[in] p pointer to a Primaries struct filled in with the primary values. + * @param[in] latitude tolerance to use while matching. A suitable value might be 0.002 + * but it depends on the application. + */ +khr_df_primaries_e findMapping(Primaries *p, float latitude) { + unsigned int i; + for (i = 0; i < sizeof(primaryMap)/sizeof(sPrimaryMapping); ++i) { + if (primaryMap[i].primaries.Rx - p->Rx <= latitude && p->Rx - primaryMap[i].primaries.Rx <= latitude && + primaryMap[i].primaries.Gx - p->Gx <= latitude && p->Gx - primaryMap[i].primaries.Gx <= latitude && + primaryMap[i].primaries.Bx - p->Bx <= latitude && p->Bx - primaryMap[i].primaries.Bx <= latitude && + primaryMap[i].primaries.Wx - p->Wx <= latitude && p->Wx - primaryMap[i].primaries.Wx <= latitude) { + return primaryMap[i].dfPrimaryEnum; + } + } + /* No match */ + return KHR_DF_PRIMARIES_UNSPECIFIED; +} diff --git a/thirdparty/libktx/lib/dfdutils/createdfd.c b/thirdparty/libktx/lib/dfdutils/createdfd.c new file mode 100644 index 0000000000..ea00d8d745 --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/createdfd.c @@ -0,0 +1,659 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * @brief Utilities for creating data format descriptors. + */ + +/* + * Author: Andrew Garrard + */ + +#include <stdlib.h> +#include <KHR/khr_df.h> + +#include "dfd.h" + +typedef enum { i_COLOR, i_NON_COLOR } channels_infotype; + +static uint32_t *writeHeader(int numSamples, int bytes, int suffix, + channels_infotype infotype) +{ + uint32_t *DFD = (uint32_t *) malloc(sizeof(uint32_t) * + (1 + KHR_DF_WORD_SAMPLESTART + + numSamples * KHR_DF_WORD_SAMPLEWORDS)); + uint32_t* BDFD = DFD+1; + DFD[0] = sizeof(uint32_t) * + (1 + KHR_DF_WORD_SAMPLESTART + + numSamples * KHR_DF_WORD_SAMPLEWORDS); + BDFD[KHR_DF_WORD_VENDORID] = + (KHR_DF_VENDORID_KHRONOS << KHR_DF_SHIFT_VENDORID) | + (KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT << KHR_DF_SHIFT_DESCRIPTORTYPE); + BDFD[KHR_DF_WORD_VERSIONNUMBER] = + (KHR_DF_VERSIONNUMBER_LATEST << KHR_DF_SHIFT_VERSIONNUMBER) | + (((uint32_t)sizeof(uint32_t) * + (KHR_DF_WORD_SAMPLESTART + + numSamples * KHR_DF_WORD_SAMPLEWORDS) + << KHR_DF_SHIFT_DESCRIPTORBLOCKSIZE)); + BDFD[KHR_DF_WORD_MODEL] = + ((KHR_DF_MODEL_RGBSDA << KHR_DF_SHIFT_MODEL) | /* Only supported model */ + (KHR_DF_FLAG_ALPHA_STRAIGHT << KHR_DF_SHIFT_FLAGS)); + if (infotype == i_COLOR) { + BDFD[KHR_DF_WORD_PRIMARIES] |= KHR_DF_PRIMARIES_BT709 << KHR_DF_SHIFT_PRIMARIES; /* Assumed */ + } else { + BDFD[KHR_DF_WORD_PRIMARIES] |= KHR_DF_PRIMARIES_UNSPECIFIED << KHR_DF_SHIFT_PRIMARIES; + } + if (suffix == s_SRGB) { + BDFD[KHR_DF_WORD_TRANSFER] |= KHR_DF_TRANSFER_SRGB << KHR_DF_SHIFT_TRANSFER; + } else { + BDFD[KHR_DF_WORD_TRANSFER] |= KHR_DF_TRANSFER_LINEAR << KHR_DF_SHIFT_TRANSFER; + } + BDFD[KHR_DF_WORD_TEXELBLOCKDIMENSION0] = 0; /* Only 1x1x1x1 texel blocks supported */ + BDFD[KHR_DF_WORD_BYTESPLANE0] = bytes; /* bytesPlane0 = bytes, bytesPlane3..1 = 0 */ + BDFD[KHR_DF_WORD_BYTESPLANE4] = 0; /* bytesPlane7..5 = 0 */ + return DFD; +} + +static uint32_t setChannelFlags(uint32_t channel, enum VkSuffix suffix) +{ + switch (suffix) { + case s_UNORM: break; + case s_SNORM: + channel |= + KHR_DF_SAMPLE_DATATYPE_SIGNED; + break; + case s_USCALED: break; + case s_SSCALED: + channel |= + KHR_DF_SAMPLE_DATATYPE_SIGNED; + break; + case s_UINT: break; + case s_SINT: + channel |= + KHR_DF_SAMPLE_DATATYPE_SIGNED; + break; + case s_SFLOAT: + channel |= + KHR_DF_SAMPLE_DATATYPE_FLOAT | + KHR_DF_SAMPLE_DATATYPE_SIGNED; + break; + case s_UFLOAT: + channel |= + KHR_DF_SAMPLE_DATATYPE_FLOAT; + break; + case s_SRGB: + if (channel == KHR_DF_CHANNEL_RGBSDA_ALPHA) { + channel |= KHR_DF_SAMPLE_DATATYPE_LINEAR; + } + break; + } + return channel; +} + +static void writeSample(uint32_t *DFD, int sampleNo, int channel, + int bits, int offset, + int topSample, int bottomSample, enum VkSuffix suffix) +{ + // Use this to avoid type-punning complaints from the gcc optimizer + // with -Wall. + union { + uint32_t i; + float f; + } lower, upper; + uint32_t *sample = DFD + 1 + KHR_DF_WORD_SAMPLESTART + sampleNo * KHR_DF_WORD_SAMPLEWORDS; + if (channel == 3) channel = KHR_DF_CHANNEL_RGBSDA_ALPHA; + + if (channel == 3) channel = KHR_DF_CHANNEL_RGBSDA_ALPHA; + channel = setChannelFlags(channel, suffix); + + sample[KHR_DF_SAMPLEWORD_BITOFFSET] = + (offset << KHR_DF_SAMPLESHIFT_BITOFFSET) | + ((bits - 1) << KHR_DF_SAMPLESHIFT_BITLENGTH) | + (channel << KHR_DF_SAMPLESHIFT_CHANNELID); + + sample[KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL] = 0; + + switch (suffix) { + case s_UNORM: + case s_SRGB: + default: + if (bits > 32) { + upper.i = 0xFFFFFFFFU; + } else { + upper.i = (uint32_t)((1U << bits) - 1U); + } + lower.i = 0U; + break; + case s_SNORM: + if (bits > 32) { + upper.i = 0x7FFFFFFF; + } else { + upper.i = topSample ? (1U << (bits - 1)) - 1 : (1U << bits) - 1; + } + lower.i = ~upper.i; + if (bottomSample) lower.i += 1; + break; + case s_USCALED: + case s_UINT: + upper.i = bottomSample ? 1U : 0U; + lower.i = 0U; + break; + case s_SSCALED: + case s_SINT: + upper.i = bottomSample ? 1U : 0U; + lower.i = ~0U; + break; + case s_SFLOAT: + upper.f = 1.0f; + lower.f = -1.0f; + break; + case s_UFLOAT: + upper.f = 1.0f; + lower.f = 0.0f; + break; + } + sample[KHR_DF_SAMPLEWORD_SAMPLELOWER] = lower.i; + sample[KHR_DF_SAMPLEWORD_SAMPLEUPPER] = upper.i; +} + +/** + * @~English + * @brief Create a Data Format Descriptor for an unpacked format. + * + * @param bigEndian Set to 1 for big-endian byte ordering and + 0 for little-endian byte ordering. + * @param numChannels The number of color channels. + * @param bytes The number of bytes per channel. + * @param redBlueSwap Normally channels appear in consecutive R, G, B, A order + * in memory; redBlueSwap inverts red and blue, allowing + * B, G, R, A. + * @param suffix Indicates the format suffix for the type. + * + * @return A data format descriptor in malloc'd data. The caller is responsible + * for freeing the descriptor. + **/ +uint32_t *createDFDUnpacked(int bigEndian, int numChannels, int bytes, + int redBlueSwap, enum VkSuffix suffix) +{ + uint32_t *DFD; + if (bigEndian) { + int channelCounter, channelByte; + /* Number of samples = number of channels * bytes per channel */ + DFD = writeHeader(numChannels * bytes, numChannels * bytes, suffix, i_COLOR); + /* First loop over the channels */ + for (channelCounter = 0; channelCounter < numChannels; ++channelCounter) { + int channel = channelCounter; + if (redBlueSwap && (channel == 0 || channel == 2)) { + channel ^= 2; + } + /* Loop over the bytes that constitute a channel */ + for (channelByte = 0; channelByte < bytes; ++channelByte) { + writeSample(DFD, channelCounter * bytes + channelByte, channel, + 8, 8 * (channelCounter * bytes + bytes - channelByte - 1), + channelByte == bytes-1, channelByte == 0, suffix); + } + } + + } else { /* Little-endian */ + + int sampleCounter; + /* One sample per channel */ + DFD = writeHeader(numChannels, numChannels * bytes, suffix, i_COLOR); + for (sampleCounter = 0; sampleCounter < numChannels; ++sampleCounter) { + int channel = sampleCounter; + if (redBlueSwap && (channel == 0 || channel == 2)) { + channel ^= 2; + } + writeSample(DFD, sampleCounter, channel, + 8 * bytes, 8 * sampleCounter * bytes, + 1, 1, suffix); + } + } + return DFD; +} + +/** + * @~English + * @brief Create a Data Format Descriptor for a packed format. + * + * @param bigEndian Big-endian flag: Set to 1 for big-endian byte ordering and + * 0 for little-endian byte ordering. + * @param numChannels The number of color channels. + * @param bits[] An array of length numChannels. + * Each entry is the number of bits composing the channel, in + * order starting at bit 0 of the packed type. + * @param channels[] An array of length numChannels. + * Each entry enumerates the channel type: 0 = red, 1 = green, + * 2 = blue, 15 = alpha, in order starting at bit 0 of the + * packed type. These values match channel IDs for RGBSDA in + * the Khronos Data Format header. To simplify iteration + * through channels, channel id 3 is a synonym for alpha. + * @param suffix Indicates the format suffix for the type. + * + * @return A data format descriptor in malloc'd data. The caller is responsible + * for freeing the descriptor. + **/ +uint32_t *createDFDPacked(int bigEndian, int numChannels, + int bits[], int channels[], + enum VkSuffix suffix) +{ + uint32_t *DFD = 0; + if (numChannels == 6) { + /* Special case E5B9G9R9 */ + DFD = writeHeader(numChannels, 4, s_UFLOAT, i_COLOR); + writeSample(DFD, 0, 0, + 9, 0, + 1, 1, s_UNORM); + KHR_DFDSETSVAL((DFD+1), 0, SAMPLEUPPER, 8448); + writeSample(DFD, 1, 0 | KHR_DF_SAMPLE_DATATYPE_EXPONENT, + 5, 27, + 1, 1, s_UNORM); + KHR_DFDSETSVAL((DFD+1), 1, SAMPLELOWER, 15); + KHR_DFDSETSVAL((DFD+1), 1, SAMPLEUPPER, 31); + writeSample(DFD, 2, 1, + 9, 9, + 1, 1, s_UNORM); + KHR_DFDSETSVAL((DFD+1), 2, SAMPLEUPPER, 8448); + writeSample(DFD, 3, 1 | KHR_DF_SAMPLE_DATATYPE_EXPONENT, + 5, 27, + 1, 1, s_UNORM); + KHR_DFDSETSVAL((DFD+1), 3, SAMPLELOWER, 15); + KHR_DFDSETSVAL((DFD+1), 3, SAMPLEUPPER, 31); + writeSample(DFD, 4, 2, + 9, 18, + 1, 1, s_UNORM); + KHR_DFDSETSVAL((DFD+1), 4, SAMPLEUPPER, 8448); + writeSample(DFD, 5, 2 | KHR_DF_SAMPLE_DATATYPE_EXPONENT, + 5, 27, + 1, 1, s_UNORM); + KHR_DFDSETSVAL((DFD+1), 5, SAMPLELOWER, 15); + KHR_DFDSETSVAL((DFD+1), 5, SAMPLEUPPER, 31); + } else if (bigEndian) { + /* No packed format is larger than 32 bits. */ + /* No packed channel crosses more than two bytes. */ + int totalBits = 0; + int bitChannel[32]; + int beChannelStart[4]; + int channelCounter; + int bitOffset = 0; + int BEMask; + int numSamples = numChannels; + int sampleCounter; + for (channelCounter = 0; channelCounter < numChannels; ++channelCounter) { + beChannelStart[channelCounter] = totalBits; + totalBits += bits[channelCounter]; + } + BEMask = (totalBits - 1) & 0x18; + for (channelCounter = 0; channelCounter < numChannels; ++channelCounter) { + bitChannel[bitOffset ^ BEMask] = channelCounter; + if (((bitOffset + bits[channelCounter] - 1) & ~7) != (bitOffset & ~7)) { + /* Continuation sample */ + bitChannel[((bitOffset + bits[channelCounter] - 1) & ~7) ^ BEMask] = channelCounter; + numSamples++; + } + bitOffset += bits[channelCounter]; + } + DFD = writeHeader(numSamples, totalBits >> 3, suffix, i_COLOR); + + sampleCounter = 0; + for (bitOffset = 0; bitOffset < totalBits;) { + if (bitChannel[bitOffset] == -1) { + /* Done this bit, so this is the lower half of something. */ + /* We must therefore jump to the end of the byte and continue. */ + bitOffset = (bitOffset + 8) & ~7; + } else { + /* Start of a channel? */ + int thisChannel = bitChannel[bitOffset]; + if ((beChannelStart[thisChannel] ^ BEMask) == bitOffset) { + /* Must be just one sample if we hit it first. */ + writeSample(DFD, sampleCounter++, channels[thisChannel], + bits[thisChannel], bitOffset, + 1, 1, suffix); + bitOffset += bits[thisChannel]; + } else { + /* Two samples. Move to the end of the first one we hit when we're done. */ + int firstSampleBits = 8 - (beChannelStart[thisChannel] & 0x7); /* Rest of the byte */ + int secondSampleBits = bits[thisChannel] - firstSampleBits; /* Rest of the bits */ + writeSample(DFD, sampleCounter++, channels[thisChannel], + firstSampleBits, beChannelStart[thisChannel] ^ BEMask, + 0, 1, suffix); + /* Mark that we've already handled this sample */ + bitChannel[beChannelStart[thisChannel] ^ BEMask] = -1; + writeSample(DFD, sampleCounter++, channels[thisChannel], + secondSampleBits, bitOffset, + 1, 0, suffix); + bitOffset += secondSampleBits; + } + } + } + + } else { /* Little-endian */ + + int sampleCounter; + int totalBits = 0; + int bitOffset = 0; + for (sampleCounter = 0; sampleCounter < numChannels; ++sampleCounter) { + totalBits += bits[sampleCounter]; + } + + /* One sample per channel */ + DFD = writeHeader(numChannels, totalBits >> 3, suffix, i_COLOR); + for (sampleCounter = 0; sampleCounter < numChannels; ++sampleCounter) { + writeSample(DFD, sampleCounter, channels[sampleCounter], + bits[sampleCounter], bitOffset, + 1, 1, suffix); + bitOffset += bits[sampleCounter]; + } + } + return DFD; +} + +static khr_df_model_e compModelMapping[] = { + KHR_DF_MODEL_BC1A, /*!< BC1, aka DXT1, no alpha. */ + KHR_DF_MODEL_BC1A, /*!< BC1, aka DXT1, punch-through alpha. */ + KHR_DF_MODEL_BC2, /*!< BC2, aka DXT2 and DXT3. */ + KHR_DF_MODEL_BC3, /*!< BC3, aka DXT4 and DXT5. */ + KHR_DF_MODEL_BC4, /*!< BC4. */ + KHR_DF_MODEL_BC5, /*!< BC5. */ + KHR_DF_MODEL_BC6H, /*!< BC6h HDR format. */ + KHR_DF_MODEL_BC7, /*!< BC7. */ + KHR_DF_MODEL_ETC2, /*!< ETC2 no alpha. */ + KHR_DF_MODEL_ETC2, /*!< ETC2 punch-through alpha. */ + KHR_DF_MODEL_ETC2, /*!< ETC2 independent alpha. */ + KHR_DF_MODEL_ETC2, /*!< R11 ETC2 single-channel. */ + KHR_DF_MODEL_ETC2, /*!< R11G11 ETC2 dual-channel. */ + KHR_DF_MODEL_ASTC, /*!< ASTC. */ + KHR_DF_MODEL_ETC1S, /*!< ETC1S. */ + KHR_DF_MODEL_PVRTC, /*!< PVRTC(1). */ + KHR_DF_MODEL_PVRTC2 /*!< PVRTC2. */ +}; + +static uint32_t compSampleCount[] = { + 1U, /*!< BC1, aka DXT1, no alpha. */ + 1U, /*!< BC1, aka DXT1, punch-through alpha. */ + 2U, /*!< BC2, aka DXT2 and DXT3. */ + 2U, /*!< BC3, aka DXT4 and DXT5. */ + 1U, /*!< BC4. */ + 2U, /*!< BC5. */ + 1U, /*!< BC6h HDR format. */ + 1U, /*!< BC7. */ + 1U, /*!< ETC2 no alpha. */ + 2U, /*!< ETC2 punch-through alpha. */ + 2U, /*!< ETC2 independent alpha. */ + 1U, /*!< R11 ETC2 single-channel. */ + 2U, /*!< R11G11 ETC2 dual-channel. */ + 1U, /*!< ASTC. */ + 1U, /*!< ETC1S. */ + 1U, /*!< PVRTC. */ + 1U /*!< PVRTC2. */ +}; + +static khr_df_model_channels_e compFirstChannel[] = { + KHR_DF_CHANNEL_BC1A_COLOR, /*!< BC1, aka DXT1, no alpha. */ + KHR_DF_CHANNEL_BC1A_ALPHAPRESENT, /*!< BC1, aka DXT1, punch-through alpha. */ + KHR_DF_CHANNEL_BC2_ALPHA, /*!< BC2, aka DXT2 and DXT3. */ + KHR_DF_CHANNEL_BC3_ALPHA, /*!< BC3, aka DXT4 and DXT5. */ + KHR_DF_CHANNEL_BC4_DATA, /*!< BC4. */ + KHR_DF_CHANNEL_BC5_RED, /*!< BC5. */ + KHR_DF_CHANNEL_BC6H_COLOR, /*!< BC6h HDR format. */ + KHR_DF_CHANNEL_BC7_COLOR, /*!< BC7. */ + KHR_DF_CHANNEL_ETC2_COLOR, /*!< ETC2 no alpha. */ + KHR_DF_CHANNEL_ETC2_COLOR, /*!< ETC2 punch-through alpha. */ + KHR_DF_CHANNEL_ETC2_ALPHA, /*!< ETC2 independent alpha. */ + KHR_DF_CHANNEL_ETC2_RED, /*!< R11 ETC2 single-channel. */ + KHR_DF_CHANNEL_ETC2_RED, /*!< R11G11 ETC2 dual-channel. */ + KHR_DF_CHANNEL_ASTC_DATA, /*!< ASTC. */ + KHR_DF_CHANNEL_ETC1S_RGB, /*!< ETC1S. */ + KHR_DF_CHANNEL_PVRTC_COLOR, /*!< PVRTC. */ + KHR_DF_CHANNEL_PVRTC2_COLOR /*!< PVRTC2. */ +}; + +static khr_df_model_channels_e compSecondChannel[] = { + KHR_DF_CHANNEL_BC1A_COLOR, /*!< BC1, aka DXT1, no alpha. */ + KHR_DF_CHANNEL_BC1A_ALPHAPRESENT, /*!< BC1, aka DXT1, punch-through alpha. */ + KHR_DF_CHANNEL_BC2_COLOR, /*!< BC2, aka DXT2 and DXT3. */ + KHR_DF_CHANNEL_BC3_COLOR, /*!< BC3, aka DXT4 and DXT5. */ + KHR_DF_CHANNEL_BC4_DATA, /*!< BC4. */ + KHR_DF_CHANNEL_BC5_GREEN, /*!< BC5. */ + KHR_DF_CHANNEL_BC6H_COLOR, /*!< BC6h HDR format. */ + KHR_DF_CHANNEL_BC7_COLOR, /*!< BC7. */ + KHR_DF_CHANNEL_ETC2_COLOR, /*!< ETC2 no alpha. */ + KHR_DF_CHANNEL_ETC2_ALPHA, /*!< ETC2 punch-through alpha. */ + KHR_DF_CHANNEL_ETC2_COLOR, /*!< ETC2 independent alpha. */ + KHR_DF_CHANNEL_ETC2_RED, /*!< R11 ETC2 single-channel. */ + KHR_DF_CHANNEL_ETC2_GREEN, /*!< R11G11 ETC2 dual-channel. */ + KHR_DF_CHANNEL_ASTC_DATA, /*!< ASTC. */ + KHR_DF_CHANNEL_ETC1S_RGB, /*!< ETC1S. */ + KHR_DF_CHANNEL_PVRTC_COLOR, /*!< PVRTC. */ + KHR_DF_CHANNEL_PVRTC2_COLOR /*!< PVRTC2. */ +}; + +static uint32_t compSecondChannelOffset[] = { + 0U, /*!< BC1, aka DXT1, no alpha. */ + 0U, /*!< BC1, aka DXT1, punch-through alpha. */ + 64U, /*!< BC2, aka DXT2 and DXT3. */ + 64U, /*!< BC3, aka DXT4 and DXT5. */ + 0U, /*!< BC4. */ + 64U, /*!< BC5. */ + 0U, /*!< BC6h HDR format. */ + 0U, /*!< BC7. */ + 0U, /*!< ETC2 no alpha. */ + 0U, /*!< ETC2 punch-through alpha. */ + 64U, /*!< ETC2 independent alpha. */ + 0U, /*!< R11 ETC2 single-channel. */ + 64U, /*!< R11G11 ETC2 dual-channel. */ + 0U, /*!< ASTC. */ + 0U, /*!< ETC1S. */ + 0U, /*!< PVRTC. */ + 0U /*!< PVRTC2. */ +}; + +static uint32_t compChannelBits[] = { + 64U, /*!< BC1, aka DXT1, no alpha. */ + 64U, /*!< BC1, aka DXT1, punch-through alpha. */ + 64U, /*!< BC2, aka DXT2 and DXT3. */ + 64U, /*!< BC3, aka DXT4 and DXT5. */ + 64U, /*!< BC4. */ + 64U, /*!< BC5. */ + 128U, /*!< BC6h HDR format. */ + 128U, /*!< BC7. */ + 64U, /*!< ETC2 no alpha. */ + 64U, /*!< ETC2 punch-through alpha. */ + 64U, /*!< ETC2 independent alpha. */ + 64U, /*!< R11 ETC2 single-channel. */ + 64U, /*!< R11G11 ETC2 dual-channel. */ + 128U, /*!< ASTC. */ + 64U, /*!< ETC1S. */ + 64U, /*!< PVRTC. */ + 64U /*!< PVRTC2. */ +}; + +static uint32_t compBytes[] = { + 8U, /*!< BC1, aka DXT1, no alpha. */ + 8U, /*!< BC1, aka DXT1, punch-through alpha. */ + 16U, /*!< BC2, aka DXT2 and DXT3. */ + 16U, /*!< BC3, aka DXT4 and DXT5. */ + 8U, /*!< BC4. */ + 16U, /*!< BC5. */ + 16U, /*!< BC6h HDR format. */ + 16U, /*!< BC7. */ + 8U, /*!< ETC2 no alpha. */ + 8U, /*!< ETC2 punch-through alpha. */ + 16U, /*!< ETC2 independent alpha. */ + 8U, /*!< R11 ETC2 single-channel. */ + 16U, /*!< R11G11 ETC2 dual-channel. */ + 16U, /*!< ASTC. */ + 8U, /*!< ETC1S. */ + 8U, /*!< PVRTC. */ + 8U /*!< PVRTC2. */ +}; + +/** + * @~English + * @brief Create a Data Format Descriptor for a compressed format. + * + * @param compScheme Vulkan-style compression scheme enumeration. + * @param bwidth Block width in texel coordinates. + * @param bheight Block height in texel coordinates. + * @param bdepth Block depth in texel coordinates. + * @author Mark Callow, Edgewise Consulting. + * @param suffix Indicates the format suffix for the type. + * + * @return A data format descriptor in malloc'd data. The caller is responsible + * for freeing the descriptor. + **/ +uint32_t *createDFDCompressed(enum VkCompScheme compScheme, int bwidth, int bheight, int bdepth, + enum VkSuffix suffix) +{ + uint32_t *DFD = 0; + uint32_t numSamples = compSampleCount[compScheme]; + uint32_t* BDFD; + uint32_t *sample; + uint32_t channel; + // Use union to avoid type-punning complaints from gcc optimizer + // with -Wall. + union { + uint32_t i; + float f; + } lower, upper; + + DFD = (uint32_t *) malloc(sizeof(uint32_t) * + (1 + KHR_DF_WORD_SAMPLESTART + + numSamples * KHR_DF_WORD_SAMPLEWORDS)); + BDFD = DFD+1; + DFD[0] = sizeof(uint32_t) * + (1 + KHR_DF_WORD_SAMPLESTART + + numSamples * KHR_DF_WORD_SAMPLEWORDS); + BDFD[KHR_DF_WORD_VENDORID] = + (KHR_DF_VENDORID_KHRONOS << KHR_DF_SHIFT_VENDORID) | + (KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT << KHR_DF_SHIFT_DESCRIPTORTYPE); + BDFD[KHR_DF_WORD_VERSIONNUMBER] = + (KHR_DF_VERSIONNUMBER_LATEST << KHR_DF_SHIFT_VERSIONNUMBER) | + (((uint32_t)sizeof(uint32_t) * + (KHR_DF_WORD_SAMPLESTART + + numSamples * KHR_DF_WORD_SAMPLEWORDS) + << KHR_DF_SHIFT_DESCRIPTORBLOCKSIZE)); + BDFD[KHR_DF_WORD_MODEL] = + ((compModelMapping[compScheme] << KHR_DF_SHIFT_MODEL) | + (KHR_DF_PRIMARIES_BT709 << KHR_DF_SHIFT_PRIMARIES) | /* Assumed */ + (KHR_DF_FLAG_ALPHA_STRAIGHT << KHR_DF_SHIFT_FLAGS)); + + if (suffix == s_SRGB) { + BDFD[KHR_DF_WORD_TRANSFER] |= KHR_DF_TRANSFER_SRGB << KHR_DF_SHIFT_TRANSFER; + } else { + BDFD[KHR_DF_WORD_TRANSFER] |= KHR_DF_TRANSFER_LINEAR << KHR_DF_SHIFT_TRANSFER; + } + BDFD[KHR_DF_WORD_TEXELBLOCKDIMENSION0] = + (bwidth - 1) | ((bheight - 1) << KHR_DF_SHIFT_TEXELBLOCKDIMENSION1) | ((bdepth - 1) << KHR_DF_SHIFT_TEXELBLOCKDIMENSION2); + /* bytesPlane0 = bytes, bytesPlane3..1 = 0 */ + BDFD[KHR_DF_WORD_BYTESPLANE0] = compBytes[compScheme]; + BDFD[KHR_DF_WORD_BYTESPLANE4] = 0; /* bytesPlane7..5 = 0 */ + + sample = BDFD + KHR_DF_WORD_SAMPLESTART; + channel = compFirstChannel[compScheme]; + channel = setChannelFlags(channel, suffix); + + sample[KHR_DF_SAMPLEWORD_BITOFFSET] = + (0 << KHR_DF_SAMPLESHIFT_BITOFFSET) | + ((compChannelBits[compScheme] - 1) << KHR_DF_SAMPLESHIFT_BITLENGTH) | + (channel << KHR_DF_SAMPLESHIFT_CHANNELID); + + sample[KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL] = 0; + switch (suffix) { + case s_UNORM: + case s_SRGB: + default: + upper.i = 0xFFFFFFFFU; + lower.i = 0U; + break; + case s_SNORM: + upper.i = 0x7FFFFFFF; + lower.i = ~upper.i; + break; + case s_USCALED: + case s_UINT: + upper.i = 1U; + lower.i = 0U; + break; + case s_SSCALED: + case s_SINT: + upper.i = 1U; + lower.i = ~0U; + break; + case s_SFLOAT: + upper.f = 1.0f; + lower.f = -1.0f; + break; + case s_UFLOAT: + upper.f = 1.0f; + lower.f = 0.0f; + break; + } + sample[KHR_DF_SAMPLEWORD_SAMPLELOWER] = lower.i; + sample[KHR_DF_SAMPLEWORD_SAMPLEUPPER] = upper.i; + + if (compSampleCount[compScheme] > 1) { + sample += KHR_DF_WORD_SAMPLEWORDS; + channel = compSecondChannel[compScheme]; + channel = setChannelFlags(channel, suffix); + + sample[KHR_DF_SAMPLEWORD_BITOFFSET] = + (compSecondChannelOffset[compScheme] << KHR_DF_SAMPLESHIFT_BITOFFSET) | + ((compChannelBits[compScheme] - 1) << KHR_DF_SAMPLESHIFT_BITLENGTH) | + (channel << KHR_DF_SAMPLESHIFT_CHANNELID); + + sample[KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL] = 0; + + sample[KHR_DF_SAMPLEWORD_SAMPLELOWER] = lower.i; + sample[KHR_DF_SAMPLEWORD_SAMPLEUPPER] = upper.i; + } + return DFD; +} + +/** + * @~English + * @brief Create a Data Format Descriptor for a depth-stencil format. + * + * @param depthBits The numeber of bits in the depth channel. + * @param stencilBits The numeber of bits in the stencil channel. + * @param sizeBytes The total byte size of the texel. + * + * @return A data format descriptor in malloc'd data. The caller is responsible + * for freeing the descriptor. + **/ +uint32_t *createDFDDepthStencil(int depthBits, + int stencilBits, + int sizeBytes) +{ + /* N.B. Little-endian is assumed. */ + uint32_t *DFD = 0; + DFD = writeHeader((depthBits > 0) + (stencilBits > 0), + sizeBytes, s_UNORM, i_NON_COLOR); + if (depthBits == 32) { + writeSample(DFD, 0, KHR_DF_CHANNEL_RGBSDA_DEPTH, + 32, 0, + 1, 1, s_SFLOAT); + } else if (depthBits > 0) { + writeSample(DFD, 0, KHR_DF_CHANNEL_RGBSDA_DEPTH, + depthBits, 0, + 1, 1, s_UNORM); + } + if (stencilBits > 0) { + if (depthBits > 0) { + writeSample(DFD, 1, KHR_DF_CHANNEL_RGBSDA_STENCIL, + stencilBits, depthBits, + 1, 1, s_UINT); + } else { + writeSample(DFD, 0, KHR_DF_CHANNEL_RGBSDA_STENCIL, + stencilBits, 0, + 1, 1, s_UINT); + } + } + return DFD; +} diff --git a/thirdparty/libktx/lib/dfdutils/dfd.h b/thirdparty/libktx/lib/dfdutils/dfd.h new file mode 100644 index 0000000000..3ba0249dbb --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/dfd.h @@ -0,0 +1,170 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * @brief Header file defining the data format descriptor utilities API. + */ + +/* + * Author: Andrew Garrard + */ + +#ifndef _DFD_H_ +#define _DFD_H_ + +#include <KHR/khr_df.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** Qualifier suffix to the format, in Vulkan terms. */ +enum VkSuffix { + s_UNORM, /*!< Unsigned normalized format. */ + s_SNORM, /*!< Signed normalized format. */ + s_USCALED, /*!< Unsigned scaled format. */ + s_SSCALED, /*!< Signed scaled format. */ + s_UINT, /*!< Unsigned integer format. */ + s_SINT, /*!< Signed integer format. */ + s_SFLOAT, /*!< Signed float format. */ + s_UFLOAT, /*!< Unsigned float format. */ + s_SRGB /*!< sRGB normalized format. */ +}; + +/** Compression scheme, in Vulkan terms. */ +enum VkCompScheme { + c_BC1_RGB, /*!< BC1, aka DXT1, no alpha. */ + c_BC1_RGBA, /*!< BC1, aka DXT1, punch-through alpha. */ + c_BC2, /*!< BC2, aka DXT2 and DXT3. */ + c_BC3, /*!< BC3, aka DXT4 and DXT5. */ + c_BC4, /*!< BC4. */ + c_BC5, /*!< BC5. */ + c_BC6H, /*!< BC6h HDR format. */ + c_BC7, /*!< BC7. */ + c_ETC2_R8G8B8, /*!< ETC2 no alpha. */ + c_ETC2_R8G8B8A1, /*!< ETC2 punch-through alpha. */ + c_ETC2_R8G8B8A8, /*!< ETC2 independent alpha. */ + c_EAC_R11, /*!< R11 ETC2 single-channel. */ + c_EAC_R11G11, /*!< R11G11 ETC2 dual-channel. */ + c_ASTC, /*!< ASTC. */ + c_ETC1S, /*!< ETC1S. */ + c_PVRTC, /*!< PVRTC(1). */ + c_PVRTC2 /*!< PVRTC2. */ +}; + +typedef unsigned int uint32_t; + +#if !defined(LIBKTX) +#include <vulkan/vulkan_core.h> +#else +#include "../vkformat_enum.h" +#endif + +uint32_t* vk2dfd(enum VkFormat format); + +/* Create a Data Format Descriptor for an unpacked format. */ +uint32_t *createDFDUnpacked(int bigEndian, int numChannels, int bytes, + int redBlueSwap, enum VkSuffix suffix); + +/* Create a Data Format Descriptor for a packed format. */ +uint32_t *createDFDPacked(int bigEndian, int numChannels, + int bits[], int channels[], + enum VkSuffix suffix); + +/* Create a Data Format Descriptor for a compressed format. */ +uint32_t *createDFDCompressed(enum VkCompScheme compScheme, + int bwidth, int bheight, int bdepth, + enum VkSuffix suffix); + +/* Create a Data Format Descriptor for a depth/stencil format. */ +uint32_t *createDFDDepthStencil(int depthBits, + int stencilBits, + int sizeBytes); + +/** @brief Result of interpreting the data format descriptor. */ +enum InterpretDFDResult { + i_LITTLE_ENDIAN_FORMAT_BIT = 0, /*!< Confirmed little-endian (default for 8bpc). */ + i_BIG_ENDIAN_FORMAT_BIT = 1, /*!< Confirmed big-endian. */ + i_PACKED_FORMAT_BIT = 2, /*!< Packed format. */ + i_SRGB_FORMAT_BIT = 4, /*!< sRGB transfer function. */ + i_NORMALIZED_FORMAT_BIT = 8, /*!< Normalized (UNORM or SNORM). */ + i_SIGNED_FORMAT_BIT = 16, /*!< Format is signed. */ + i_FLOAT_FORMAT_BIT = 32, /*!< Format is floating point. */ + i_UNSUPPORTED_ERROR_BIT = 64, /*!< Format not successfully interpreted. */ + /** "NONTRIVIAL_ENDIANNESS" means not big-endian, not little-endian + * (a channel has bits that are not consecutive in either order). **/ + i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS = i_UNSUPPORTED_ERROR_BIT, + /** "MULTIPLE_SAMPLE_LOCATIONS" is an error because only single-sample + * texel blocks (with coordinates 0,0,0,0 for all samples) are supported. **/ + i_UNSUPPORTED_MULTIPLE_SAMPLE_LOCATIONS = i_UNSUPPORTED_ERROR_BIT + 1, + /** "MULTIPLE_PLANES" is an error because only contiguous data is supported. */ + i_UNSUPPORTED_MULTIPLE_PLANES = i_UNSUPPORTED_ERROR_BIT + 2, + /** Only channels R, G, B and A are supported. */ + i_UNSUPPORTED_CHANNEL_TYPES = i_UNSUPPORTED_ERROR_BIT + 3, + /** Only channels with the same flags are supported + * (e.g. we don't support float red with integer green). */ + i_UNSUPPORTED_MIXED_CHANNELS = i_UNSUPPORTED_ERROR_BIT + 4 +}; + +/** @brief Interpretation of a channel from the data format descriptor. */ +typedef struct _InterpretedDFDChannel { + uint32_t offset; /*!< Offset in bits for packed, bytes for unpacked. */ + uint32_t size; /*!< Size in bits for packed, bytes for unpacked. */ +} InterpretedDFDChannel; + +/* Interpret a Data Format Descriptor. */ +enum InterpretDFDResult interpretDFD(const uint32_t *DFD, + InterpretedDFDChannel *R, + InterpretedDFDChannel *G, + InterpretedDFDChannel *B, + InterpretedDFDChannel *A, + uint32_t *wordBytes); + +/* Print a human-readable interpretation of a data format descriptor. */ +void printDFD(uint32_t *DFD); + +/* Get the number of components & component size from a DFD for an + * unpacked format. + */ +void +getDFDComponentInfoUnpacked(const uint32_t* DFD, uint32_t* numComponents, + uint32_t* componentByteLength); + +/* Return the number of components described by a DFD. */ +uint32_t getDFDNumComponents(const uint32_t* DFD); + +/* Recreate and return the value of bytesPlane0 as it should be for the data + * post-inflation from variable-rate compression. + */ +void +recreateBytesPlane0FromSampleInfo(const uint32_t* DFD, uint32_t* bytesPlane0); + +/** @brief Colourspace primaries information. + * + * Structure to store the 1931 CIE x,y chromaticities of the red, green, and blue + * display primaries and the reference white point of a colourspace. + */ +typedef struct _Primaries { + float Rx; /*!< Red x. */ + float Ry; /*!< Red y. */ + float Gx; /*!< Green x. */ + float Gy; /*!< Green y. */ + float Bx; /*!< Blue x. */ + float By; /*!< Blue y. */ + float Wx; /*!< White x. */ + float Wy; /*!< White y. */ +} Primaries; + +khr_df_primaries_e findMapping(Primaries *p, float latitude); + +#ifdef __cplusplus +} +#endif + +#endif /* _DFD_H_ */ diff --git a/thirdparty/libktx/lib/dfdutils/dfd2vk.inl b/thirdparty/libktx/lib/dfdutils/dfd2vk.inl new file mode 100644 index 0000000000..cfed9140e9 --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/dfd2vk.inl @@ -0,0 +1,599 @@ +/* Copyright 2019-2020 The Khronos Group Inc. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/***************************** Do not edit. ***************************** + Automatically generated by makedfd2vk.pl. + *************************************************************************/ +if (KHR_DFDVAL(dfd + 1, MODEL) == KHR_DF_MODEL_RGBSDA) { + enum InterpretDFDResult r; + InterpretedDFDChannel R = {0,0}; + InterpretedDFDChannel G = {0,0}; + InterpretedDFDChannel B = {0,0}; + InterpretedDFDChannel A = {0,0}; + uint32_t wordBytes; + + /* Special case exponent format */ + if (KHR_DFDSAMPLECOUNT(dfd + 1) == 6 && + ((KHR_DFDSVAL((dfd + 1), 1, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_EXPONENT) > 0)) { + /* The only format we expect to be encoded like this. */ + return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; + } + + /* Special case depth formats (assumed little-endian) */ + if (KHR_DFDSVAL((dfd + 1), 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_DEPTH) { + if (KHR_DFDSAMPLECOUNT((dfd + 1)) == 1) { + if (KHR_DFDSVAL((dfd + 1), 0, BITLENGTH) == 16-1) return VK_FORMAT_D16_UNORM; + if (KHR_DFDSVAL((dfd + 1), 0, BITLENGTH) == 24-1) return VK_FORMAT_X8_D24_UNORM_PACK32; + return VK_FORMAT_D32_SFLOAT; + } else { + if (KHR_DFDSVAL((dfd + 1), 0, BITLENGTH) == 16-1) return VK_FORMAT_D16_UNORM_S8_UINT; + if (KHR_DFDSVAL((dfd + 1), 0, BITLENGTH) == 24-1) return VK_FORMAT_D24_UNORM_S8_UINT; + return VK_FORMAT_D32_SFLOAT_S8_UINT; + } + } + if (KHR_DFDSVAL((dfd + 1), 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_STENCIL) { + return VK_FORMAT_S8_UINT; + } + + r = interpretDFD(dfd, &R, &G, &B, &A, &wordBytes); + + if (r & i_UNSUPPORTED_ERROR_BIT) return VK_FORMAT_UNDEFINED; + + if (r & i_PACKED_FORMAT_BIT) { + if (wordBytes == 1) return VK_FORMAT_R4G4_UNORM_PACK8; + else if (wordBytes == 2) { /* PACK16 */ + if (A.size == 4) { + if (R.offset == 12) return VK_FORMAT_R4G4B4A4_UNORM_PACK16; + else return VK_FORMAT_B4G4R4A4_UNORM_PACK16; + } else if (A.size == 0) { /* Three channels */ + if (B.offset == 0) return VK_FORMAT_R5G6B5_UNORM_PACK16; + else return VK_FORMAT_B5G6R5_UNORM_PACK16; + } else { /* Four channels, one-bit alpha */ + if (B.offset == 0) return VK_FORMAT_A1R5G5B5_UNORM_PACK16; + if (B.offset == 1) return VK_FORMAT_R5G5B5A1_UNORM_PACK16; + return VK_FORMAT_B5G5R5A1_UNORM_PACK16; + } + } else if (wordBytes == 4) { /* PACK32 */ + if (A.size == 8) { + if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_SRGB_PACK32; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_UNORM_PACK32; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_SNORM_PACK32; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_UINT_PACK32; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_SINT_PACK32; + } else if (A.size == 2 && B.offset == 0) { + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2R10G10B10_UNORM_PACK32; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2R10G10B10_SNORM_PACK32; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2R10G10B10_UINT_PACK32; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2R10G10B10_SINT_PACK32; + } else if (A.size == 2 && R.offset == 0) { + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2B10G10R10_UNORM_PACK32; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2B10G10R10_SNORM_PACK32; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2B10G10R10_UINT_PACK32; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2B10G10R10_SINT_PACK32; + } else if (R.size == 11) return VK_FORMAT_B10G11R11_UFLOAT_PACK32; + } + } else { /* Not a packed format */ + if (wordBytes == 1) { + if (A.size > 0) { /* 4 channels */ + if (R.offset == 0) { /* RGBA */ + if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_SRGB; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_SINT; + } else { /* BGRA */ + if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_SRGB; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_SINT; + } + } else if (B.size > 0) { /* 3 channels */ + if (R.offset == 0) { /* RGB */ + if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_R8G8B8_SRGB; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8_SINT; + } else { /* BGR */ + if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_B8G8R8_SRGB; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8_SINT; + } + } else if (G.size > 0) { /* 2 channels */ + if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_R8G8_SRGB; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8_SINT; + } else { /* 1 channel */ + if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_R8_SRGB; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8_SINT; + } + } else if (wordBytes == 2) { + if (A.size > 0) { /* 4 channels */ + if (R.offset == 0) { /* RGBA */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_SFLOAT; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_SINT; + } else { /* BGRA */ + } + } else if (B.size > 0) { /* 3 channels */ + if (R.offset == 0) { /* RGB */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R16G16B16_SFLOAT; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16_SINT; + } else { /* BGR */ + } + } else if (G.size > 0) { /* 2 channels */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R16G16_SFLOAT; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16_SINT; + } else { /* 1 channel */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R16_SFLOAT; + if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16_UNORM; + if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16_SNORM; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16_SINT; + } + } else if (wordBytes == 4) { + if (A.size > 0) { /* 4 channels */ + if (R.offset == 0) { /* RGBA */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R32G32B32A32_SFLOAT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32B32A32_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32B32A32_SINT; + } else { /* BGRA */ + } + } else if (B.size > 0) { /* 3 channels */ + if (R.offset == 0) { /* RGB */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R32G32B32_SFLOAT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32B32_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32B32_SINT; + } else { /* BGR */ + } + } else if (G.size > 0) { /* 2 channels */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R32G32_SFLOAT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32_SINT; + } else { /* 1 channel */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R32_SFLOAT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32_SINT; + } + } else if (wordBytes == 8) { + if (A.size > 0) { /* 4 channels */ + if (R.offset == 0) { /* RGBA */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R64G64B64A64_SFLOAT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64B64A64_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64B64A64_SINT; + } else { /* BGRA */ + } + } else if (B.size > 0) { /* 3 channels */ + if (R.offset == 0) { /* RGB */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R64G64B64_SFLOAT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64B64_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64B64_SINT; + } else { /* BGR */ + } + } else if (G.size > 0) { /* 2 channels */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R64G64_SFLOAT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64_SINT; + } else { /* 1 channel */ + if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R64_SFLOAT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64_UINT; + if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64_SINT; + } + } + } +} else if (KHR_DFDVAL((dfd + 1), MODEL) >= 128) { + const uint32_t *bdb = dfd + 1; + switch (KHR_DFDVAL(bdb, MODEL)) { + case KHR_DF_MODEL_BC1A: + if (KHR_DFDSVAL(bdb, 0, CHANNELID) == KHR_DF_CHANNEL_BC1A_COLOR) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_BC1_RGB_UNORM_BLOCK; + } else { + return VK_FORMAT_BC1_RGB_SRGB_BLOCK; + } + } else { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; + } else { + return VK_FORMAT_BC1_RGBA_SRGB_BLOCK; + } + } + case KHR_DF_MODEL_BC2: + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_BC2_UNORM_BLOCK; + } else { + return VK_FORMAT_BC2_SRGB_BLOCK; + } + case KHR_DF_MODEL_BC3: + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_BC3_UNORM_BLOCK; + } else { + return VK_FORMAT_BC3_SRGB_BLOCK; + } + case KHR_DF_MODEL_BC4: + if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) { + return VK_FORMAT_BC4_UNORM_BLOCK; + } else { + return VK_FORMAT_BC4_SNORM_BLOCK; + } + case KHR_DF_MODEL_BC5: + if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) { + return VK_FORMAT_BC5_UNORM_BLOCK; + } else { + return VK_FORMAT_BC5_SNORM_BLOCK; + } + case KHR_DF_MODEL_BC6H: + if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) { + return VK_FORMAT_BC6H_UFLOAT_BLOCK; + } else { + return VK_FORMAT_BC6H_SFLOAT_BLOCK; + } + case KHR_DF_MODEL_BC7: + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_BC7_UNORM_BLOCK; + } else { + return VK_FORMAT_BC7_SRGB_BLOCK; + } + case KHR_DF_MODEL_ETC2: + if (KHR_DFDSVAL(bdb, 0, CHANNELID) == KHR_DF_CHANNEL_ETC2_COLOR) { + if (KHR_DFDVAL(bdb, DESCRIPTORBLOCKSIZE) == 40) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; + } else { + return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; + } + } else { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; + } else { + return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; + } + } + } else if (KHR_DFDSVAL(bdb, 0, CHANNELID) == KHR_DF_CHANNEL_ETC2_ALPHA) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; + } else { + return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; + } + } else if (KHR_DFDVAL(bdb, DESCRIPTORBLOCKSIZE) == 40) { + if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) { + return VK_FORMAT_EAC_R11_UNORM_BLOCK; + } else { + return VK_FORMAT_EAC_R11_SNORM_BLOCK; + } + } else { + if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) { + return VK_FORMAT_EAC_R11G11_UNORM_BLOCK; + } else { + return VK_FORMAT_EAC_R11G11_SNORM_BLOCK; + } + } + case KHR_DF_MODEL_ASTC: + if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_FLOAT)) { + if (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 0) { + if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_4x4_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_4x4_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_5x4_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_5x4_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_5x5_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_5x5_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_6x5_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_6x5_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_6x6_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_6x6_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_8x5_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_8x5_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_8x6_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_8x6_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 7)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_8x8_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_8x8_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_10x5_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_10x5_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_10x6_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_10x6_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 7)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_10x8_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_10x8_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 9)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_10x10_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_10x10_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 11) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 9)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_12x10_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_12x10_SRGB_BLOCK; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 11) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 11)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_12x12_UNORM_BLOCK; + } else { + return VK_FORMAT_ASTC_12x12_SRGB_BLOCK; + } + } + } else { + if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 2) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT; + } + } + if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT; + } + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 5)) { + if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT; + } else { + return VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT; + } + } + } + } else { + if (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 0) { + if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3)) { + return VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3)) { + return VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) { + return VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) { + return VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) { + return VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) { + return VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) { + return VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 7)) { + return VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) { + return VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) { + return VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 7)) { + return VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 9)) { + return VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 11) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 9)) { + return VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 11) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 11)) { + return VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT; + } + } else { + if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 2) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) { + return VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) { + return VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) { + return VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) { + return VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) { + return VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) { + return VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) { + return VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) { + return VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) { + return VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) { + return VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT; + } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5) && + (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 5)) { + return VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT; + } + } + } + break; + case KHR_DF_MODEL_PVRTC: + if (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) { + if (KHR_DFDVAL(bdb, TRANSFER) == KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG; + } else { + return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG; + } + } else { + if (KHR_DFDVAL(bdb, TRANSFER) == KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG; + } else { + return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG; + } + } + case KHR_DF_MODEL_PVRTC2: + if (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) { + if (KHR_DFDVAL(bdb, TRANSFER) == KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG; + } else { + return VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG; + } + } else { + if (KHR_DFDVAL(bdb, TRANSFER) == KHR_DF_TRANSFER_SRGB) { + return VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG; + } else { + return VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG; + } + } + default: + ; + } +} +return VK_FORMAT_UNDEFINED; /* Drop-through for unmatched formats. */ diff --git a/thirdparty/libktx/lib/dfdutils/interpretdfd.c b/thirdparty/libktx/lib/dfdutils/interpretdfd.c new file mode 100644 index 0000000000..273410a18c --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/interpretdfd.c @@ -0,0 +1,345 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * @brief Utility for interpreting a data format descriptor. + * @author Andrew Garrard + */ + +#include <stdint.h> +#include <stdio.h> +#include <KHR/khr_df.h> +#include "dfd.h" + +/** + * @~English + * @brief Interpret a Data Format Descriptor for a simple format. + * + * @param DFD Pointer to a Data Format Descriptor to interpret, + described as 32-bit words in native endianness. + Note that this is the whole descriptor, not just + the basic descriptor block. + * @param R Information about the decoded red channel, if any. + * @param G Information about the decoded green channel, if any. + * @param B Information about the decoded blue channel, if any. + * @param A Information about the decoded alpha channel, if any. + * @param wordBytes Byte size of the channels (unpacked) or total size (packed). + * + * @return An enumerant describing the decoded value, + * or an error code in case of failure. + **/ +enum InterpretDFDResult interpretDFD(const uint32_t *DFD, + InterpretedDFDChannel *R, + InterpretedDFDChannel *G, + InterpretedDFDChannel *B, + InterpretedDFDChannel *A, + uint32_t *wordBytes) +{ + /* We specifically handle "simple" cases that can be translated */ + /* to things a GPU can access. For simplicity, we also ignore */ + /* the compressed formats, which are generally a single sample */ + /* (and I believe are all defined to be little-endian in their */ + /* in-memory layout, even if some documentation confuses this). */ + /* We also just worry about layout and ignore sRGB, since that's */ + /* trivial to extract anyway. */ + + /* DFD points to the whole descriptor, not the basic descriptor block. */ + /* Make everything else relative to the basic descriptor block. */ + const uint32_t *BDFDB = DFD+1; + + uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB); + + uint32_t sampleCounter; + int determinedEndianness = 0; + int determinedNormalizedness = 0; + int determinedSignedness = 0; + int determinedFloatness = 0; + enum InterpretDFDResult result = 0; /* Build this up incrementally. */ + + /* Clear these so following code doesn't get confused. */ + R->offset = R->size = 0; + G->offset = G->size = 0; + B->offset = B->size = 0; + A->offset = A->size = 0; + + /* First rule out the multiple planes case (trivially) */ + /* - that is, we check that only bytesPlane0 is non-zero. */ + /* This means we don't handle YUV even if the API could. */ + /* (We rely on KHR_DF_WORD_BYTESPLANE0..3 being the same and */ + /* KHR_DF_WORD_BYTESPLANE4..7 being the same as a short cut.) */ + if ((BDFDB[KHR_DF_WORD_BYTESPLANE0] & ~KHR_DF_MASK_BYTESPLANE0) + || BDFDB[KHR_DF_WORD_BYTESPLANE4]) return i_UNSUPPORTED_MULTIPLE_PLANES; + + /* Only support the RGB color model. */ + /* We could expand this to allow "UNSPECIFIED" as well. */ + if (KHR_DFDVAL(BDFDB, MODEL) != KHR_DF_MODEL_RGBSDA) return i_UNSUPPORTED_CHANNEL_TYPES; + + /* We only pay attention to sRGB. */ + if (KHR_DFDVAL(BDFDB, TRANSFER) == KHR_DF_TRANSFER_SRGB) result |= i_SRGB_FORMAT_BIT; + + /* We only support samples at coordinate 0,0,0,0. */ + /* (We could confirm this from texel_block_dimensions in 1.2, but */ + /* the interpretation might change in later versions.) */ + for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) { + if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEPOSITION_ALL)) + return i_UNSUPPORTED_MULTIPLE_SAMPLE_LOCATIONS; + } + + /* Set flags and check for consistency. */ + for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) { + /* Note: We're ignoring 9995, which is weird and worth special-casing */ + /* rather than trying to generalise to all float formats. */ + if (!determinedFloatness) { + if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS) + & KHR_DF_SAMPLE_DATATYPE_FLOAT) { + result |= i_FLOAT_FORMAT_BIT; + determinedFloatness = 1; + } + } else { + /* Check whether we disagree with our predetermined floatness. */ + /* Note that this could justifiably happen with (say) D24S8. */ + if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS) + & KHR_DF_SAMPLE_DATATYPE_FLOAT) { + if (!(result & i_FLOAT_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS; + } else { + if ((result & i_FLOAT_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS; + } + } + if (!determinedSignedness) { + if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS) + & KHR_DF_SAMPLE_DATATYPE_SIGNED) { + result |= i_SIGNED_FORMAT_BIT; + determinedSignedness = 1; + } + } else { + /* Check whether we disagree with our predetermined signedness. */ + if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS) + & KHR_DF_SAMPLE_DATATYPE_SIGNED) { + if (!(result & i_SIGNED_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS; + } else { + if ((result & i_SIGNED_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS; + } + } + /* We define "unnormalized" as "sample_upper = 1". */ + /* We don't check whether any non-1 normalization value is correct */ + /* (i.e. set to the maximum bit value, and check min value) on */ + /* the assumption that we're looking at a format which *came* from */ + /* an API we can support. */ + if (!determinedNormalizedness) { + /* The ambiguity here is if the bottom bit is a single-bit value, */ + /* as in RGBA 5:5:5:1, so we defer the decision if the channel only has one bit. */ + if (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) > 0) { + if ((result & i_FLOAT_FORMAT_BIT)) { + if (*(float *)(void *)&BDFDB[KHR_DF_WORD_SAMPLESTART + + KHR_DF_WORD_SAMPLEWORDS * sampleCounter + + KHR_DF_SAMPLEWORD_SAMPLEUPPER] != 1.0f) { + result |= i_NORMALIZED_FORMAT_BIT; + } + } else { + if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEUPPER) != 1U) { + result |= i_NORMALIZED_FORMAT_BIT; + } + } + determinedNormalizedness = 1; + } + } + /* Note: We don't check for inconsistent normalization, because */ + /* channels composed of multiple samples will have 0 in the */ + /* lower/upper range. */ + /* This heuristic should handle 64-bit integers, too. */ + } + + /* If this is a packed format, we work out our offsets differently. */ + /* We assume a packed format has channels that aren't byte-aligned. */ + /* If we have a format in which every channel is byte-aligned *and* packed, */ + /* we have the RGBA/ABGR ambiguity; we *probably* don't want the packed */ + /* version in this case, and if hardware has to pack it and swizzle, */ + /* that's up to the hardware to special-case. */ + for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) { + if (KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) & 0x7U) { + result |= i_PACKED_FORMAT_BIT; + /* Once we're packed, we're packed, no need to keep checking. */ + break; + } + } + + /* Remember: the canonical ordering of samples is to start with */ + /* the lowest bit of the channel/location which touches bit 0 of */ + /* the data, when the latter is concatenated in little-endian order, */ + /* and then progress until all the bits of that channel/location */ + /* have been processed. Multiple channels sharing the same source */ + /* bits are processed in channel ID order. (I should clarify this */ + /* for partially-shared data, but it doesn't really matter so long */ + /* as everything is consecutive, except to make things canonical.) */ + /* Note: For standard formats we could determine big/little-endianness */ + /* simply from whether the first sample starts in bit 0; technically */ + /* it's possible to have a format with unaligned channels wherein the */ + /* first channel starts at bit 0 and is one byte, yet other channels */ + /* take more bytes or aren't aligned (e.g. D24S8), but this should be */ + /* irrelevant for the formats that we support. */ + if ((result & i_PACKED_FORMAT_BIT)) { + /* A packed format. */ + uint32_t currentChannel = ~0U; /* Don't start matched. */ + uint32_t currentBitOffset = 0; + uint32_t currentByteOffset = 0; + uint32_t currentBitLength = 0; + *wordBytes = (BDFDB[KHR_DF_WORD_BYTESPLANE0] & 0xFFU); + for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) { + uint32_t sampleBitOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET); + uint32_t sampleByteOffset = sampleBitOffset >> 3U; + /* The sample bitLength field stores the bit length - 1. */ + uint32_t sampleBitLength = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1; + uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID); + InterpretedDFDChannel *sampleChannelPtr; + switch (sampleChannel) { + case KHR_DF_CHANNEL_RGBSDA_RED: + sampleChannelPtr = R; + break; + case KHR_DF_CHANNEL_RGBSDA_GREEN: + sampleChannelPtr = G; + break; + case KHR_DF_CHANNEL_RGBSDA_BLUE: + sampleChannelPtr = B; + break; + case KHR_DF_CHANNEL_RGBSDA_ALPHA: + sampleChannelPtr = A; + break; + default: + return i_UNSUPPORTED_CHANNEL_TYPES; + } + if (sampleChannel == currentChannel) { + /* Continuation of the same channel. */ + /* Since a big (>32-bit) channel isn't "packed", */ + /* this should only happen in big-endian, or if */ + /* we have a wacky format that we won't support. */ + if (sampleByteOffset == currentByteOffset - 1U && /* One byte earlier */ + ((currentBitOffset + currentBitLength) & 7U) == 0 && /* Already at the end of a byte */ + (sampleBitOffset & 7U) == 0) { /* Start at the beginning of the byte */ + /* All is good, continue big-endian. */ + /* N.B. We shouldn't be here if we decided we were little-endian, */ + /* so we don't bother to check that disagreement. */ + result |= i_BIG_ENDIAN_FORMAT_BIT; + determinedEndianness = 1; + } else { + /* Oh dear. */ + /* We could be little-endian, but not with any standard format. */ + /* More likely we've got something weird that we can't support. */ + return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS; + } + /* Remember where we are. */ + currentBitOffset = sampleBitOffset; + currentByteOffset = sampleByteOffset; + currentBitLength = sampleBitLength; + /* Accumulate the bit length. */ + sampleChannelPtr->size += sampleBitLength; + } else { + /* Everything is new. Hopefully. */ + currentChannel = sampleChannel; + currentBitOffset = sampleBitOffset; + currentByteOffset = sampleByteOffset; + currentBitLength = sampleBitLength; + if (sampleChannelPtr->size) { + /* Uh-oh, we've seen this channel before. */ + return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS; + } + /* For now, record the bit offset in little-endian terms, */ + /* because we may not know to reverse it yet. */ + sampleChannelPtr->offset = sampleBitOffset; + sampleChannelPtr->size = sampleBitLength; + } + } + if ((result & i_BIG_ENDIAN_FORMAT_BIT)) { + /* Our bit offsets to bit 0 of each channel are in little-endian terms. */ + /* We need to do a byte swap to work out where they should be. */ + /* We assume, for sanity, that byte sizes are a power of two for this. */ + uint32_t offsetMask = (*wordBytes - 1U) << 3U; + R->offset ^= offsetMask; + G->offset ^= offsetMask; + B->offset ^= offsetMask; + A->offset ^= offsetMask; + } + } else { + /* Not a packed format. */ + /* Everything is byte-aligned. */ + /* Question is whether there multiple samples per channel. */ + uint32_t currentChannel = ~0U; /* Don't start matched. */ + uint32_t currentByteOffset = 0; + uint32_t currentByteLength = 0; + for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) { + uint32_t sampleByteOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) >> 3U; + uint32_t sampleByteLength = (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1) >> 3U; + uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID); + InterpretedDFDChannel *sampleChannelPtr; + switch (sampleChannel) { + case KHR_DF_CHANNEL_RGBSDA_RED: + sampleChannelPtr = R; + break; + case KHR_DF_CHANNEL_RGBSDA_GREEN: + sampleChannelPtr = G; + break; + case KHR_DF_CHANNEL_RGBSDA_BLUE: + sampleChannelPtr = B; + break; + case KHR_DF_CHANNEL_RGBSDA_ALPHA: + sampleChannelPtr = A; + break; + default: + return i_UNSUPPORTED_CHANNEL_TYPES; + } + if (sampleChannel == currentChannel) { + /* Continuation of the same channel. */ + /* Either big-endian, or little-endian with a very large channel. */ + if (sampleByteOffset == currentByteOffset - 1) { /* One byte earlier */ + if (determinedEndianness && !(result & i_BIG_ENDIAN_FORMAT_BIT)) { + return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS; + } + /* All is good, continue big-endian. */ + result |= i_BIG_ENDIAN_FORMAT_BIT; + determinedEndianness = 1; + /* Update the start */ + sampleChannelPtr->offset = sampleByteOffset; + } else if (sampleByteOffset == currentByteOffset + currentByteLength) { + if (determinedEndianness && (result & i_BIG_ENDIAN_FORMAT_BIT)) { + return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS; + } + /* All is good, continue little-endian. */ + determinedEndianness = 1; + } else { + /* Oh dear. */ + /* We could be little-endian, but not with any standard format. */ + /* More likely we've got something weird that we can't support. */ + return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS; + } + /* Remember where we are. */ + currentByteOffset = sampleByteOffset; + currentByteLength = sampleByteLength; + /* Accumulate the byte length. */ + sampleChannelPtr->size += sampleByteLength; + /* Assume these are all the same. */ + *wordBytes = sampleChannelPtr->size; + } else { + /* Everything is new. Hopefully. */ + currentChannel = sampleChannel; + currentByteOffset = sampleByteOffset; + currentByteLength = sampleByteLength; + if (sampleChannelPtr->size) { + /* Uh-oh, we've seen this channel before. */ + return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS; + } + /* For now, record the byte offset in little-endian terms, */ + /* because we may not know to reverse it yet. */ + sampleChannelPtr->offset = sampleByteOffset; + sampleChannelPtr->size = sampleByteLength; + /* Assume these are all the same. */ + *wordBytes = sampleByteLength; + } + } + } + return result; +} diff --git a/thirdparty/libktx/lib/dfdutils/printdfd.c b/thirdparty/libktx/lib/dfdutils/printdfd.c new file mode 100644 index 0000000000..36d3d2c50d --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/printdfd.c @@ -0,0 +1,97 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * @brief Utilities for printing data format descriptors. + */ + +/* + * Author: Andrew Garrard + */ + +#include <stdio.h> +#include <KHR/khr_df.h> +#include "dfd.h" + +/** + * @~English + * @brief Print a human-readable interpretation of a data format descriptor. + * + * @param DFD Pointer to a data format descriptor. + **/ +void printDFD(uint32_t *DFD) +{ + uint32_t *BDB = DFD+1; + int samples = (KHR_DFDVAL(BDB, DESCRIPTORBLOCKSIZE) - 4 * KHR_DF_WORD_SAMPLESTART) / (4 * KHR_DF_WORD_SAMPLEWORDS); + int sample; + int model = KHR_DFDVAL(BDB, MODEL); + printf("DFD total bytes: %d\n", DFD[0]); + printf("BDB descriptor type 0x%04x vendor id = 0x%05x\n", + KHR_DFDVAL(BDB, DESCRIPTORTYPE), + KHR_DFDVAL(BDB, VENDORID)); + printf("Descriptor block size %d (%d samples) versionNumber = 0x%04x\n", + KHR_DFDVAL(BDB, DESCRIPTORBLOCKSIZE), + samples, + KHR_DFDVAL(BDB, VERSIONNUMBER)); + printf("Flags 0x%02x Xfer %02d Primaries %02d Model %03d\n", + KHR_DFDVAL(BDB, FLAGS), + KHR_DFDVAL(BDB, TRANSFER), + KHR_DFDVAL(BDB, PRIMARIES), + model); + printf("Dimensions: %d,%d,%d,%d\n", + KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION0) + 1, + KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION1) + 1, + KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION2) + 1, + KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION3) + 1); + printf("Plane bytes: %d,%d,%d,%d,%d,%d,%d,%d\n", + KHR_DFDVAL(BDB, BYTESPLANE0), + KHR_DFDVAL(BDB, BYTESPLANE1), + KHR_DFDVAL(BDB, BYTESPLANE2), + KHR_DFDVAL(BDB, BYTESPLANE3), + KHR_DFDVAL(BDB, BYTESPLANE4), + KHR_DFDVAL(BDB, BYTESPLANE5), + KHR_DFDVAL(BDB, BYTESPLANE6), + KHR_DFDVAL(BDB, BYTESPLANE7)); + for (sample = 0; sample < samples; ++sample) { + int channelId = KHR_DFDSVAL(BDB, sample, CHANNELID); + printf(" Sample %d\n", sample); + printf("Qualifiers %x", KHR_DFDSVAL(BDB, sample, QUALIFIERS) >> 4); + printf(" Channel 0x%x", channelId); + if (model == KHR_DF_MODEL_UASTC) { + printf(" (%s)", + channelId == KHR_DF_CHANNEL_UASTC_RRRG ? "RRRG" + : channelId == KHR_DF_CHANNEL_UASTC_RGBA ? "RGBA" + : channelId == KHR_DF_CHANNEL_UASTC_RRR ? "RRR" + : channelId == KHR_DF_CHANNEL_UASTC_RGB ? "RGB" + : channelId == KHR_DF_CHANNEL_UASTC_RG ? "RG" + : "unknown"); + } else if (model == KHR_DF_MODEL_ETC1S) { + printf(" (%s)", + channelId == KHR_DF_CHANNEL_ETC1S_AAA ? "AAA" + : channelId == KHR_DF_CHANNEL_ETC1S_GGG ? "GGG" + : channelId == KHR_DF_CHANNEL_ETC1S_RRR ? "RRR" + : channelId == KHR_DF_CHANNEL_ETC1S_RGB ? "RGB" + : "unknown"); + } else { + printf(" (%c)", + "RGB3456789abcdeA"[channelId]); + } + printf(" Length %d bits Offset %d\n", + KHR_DFDSVAL(BDB, sample, BITLENGTH) + 1, + KHR_DFDSVAL(BDB, sample, BITOFFSET)); + printf("Position: %d,%d,%d,%d\n", + KHR_DFDSVAL(BDB, sample, SAMPLEPOSITION0), + KHR_DFDSVAL(BDB, sample, SAMPLEPOSITION1), + KHR_DFDSVAL(BDB, sample, SAMPLEPOSITION2), + KHR_DFDSVAL(BDB, sample, SAMPLEPOSITION3)); + printf("Lower 0x%08x\nUpper 0x%08x\n", + KHR_DFDSVAL(BDB, sample, SAMPLELOWER), + KHR_DFDSVAL(BDB, sample, SAMPLEUPPER)); + } +} diff --git a/thirdparty/libktx/lib/dfdutils/queries.c b/thirdparty/libktx/lib/dfdutils/queries.c new file mode 100644 index 0000000000..19488f9e33 --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/queries.c @@ -0,0 +1,146 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * @brief Utilities for querying info from a data format descriptor. + * @author Mark Callow + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <KHR/khr_df.h> +#include "dfd.h" + +/** + * @~English + * @brief Get the number and size of the image components from a DFD. + * + * This simplified function is for use only with the DFDs for unpacked + * formats which means all components have the same size. + * + * @param DFD Pointer to a Data Format Descriptor to interpret, + described as 32-bit words in native endianness. + Note that this is the whole descriptor, not just + the basic descriptor block. + * @param numComponents pointer to a 32-bit word in which the number of + components will be written. + * @param componentByteLength pointer to a 32-bit word in which the size of + a component in bytes will be written. + */ +void +getDFDComponentInfoUnpacked(const uint32_t* DFD, uint32_t* numComponents, + uint32_t* componentByteLength) +{ + const uint32_t *BDFDB = DFD+1; + uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB); + uint32_t sampleCounter; + uint32_t currentChannel = ~0U; /* Don't start matched. */ + + /* This is specifically for unpacked formats which means the size of */ + /* each component is the same. */ + *numComponents = 0; + for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) { + uint32_t sampleByteLength = (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1) >> 3U; + uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID); + + if (sampleChannel == currentChannel) { + /* Continuation of the same channel. */ + /* Accumulate the byte length. */ + *componentByteLength += sampleByteLength; + } else { + /* Everything is new. Hopefully. */ + currentChannel = sampleChannel; + (*numComponents)++; + *componentByteLength = sampleByteLength; + } + } +} + +/** + * @~English + * @brief Return the number of "components" in the data. + * + * Calculates the number of uniques samples in the DFD by combining + * multiple samples for the same channel. For uncompressed colorModels + * this is the same as the number of components in the image data. For + * block-compressed color models this is the number of samples in + * the color model, typically 1 and in a few cases 2. + * + * @param DFD Pointer to a Data Format Descriptor for which, + * described as 32-bit words in native endianness. + * Note that this is the whole descriptor, not just + * the basic descriptor block. + */ +uint32_t getDFDNumComponents(const uint32_t* DFD) +{ + const uint32_t *BDFDB = DFD+1; + uint32_t currentChannel = ~0U; /* Don't start matched. */ + uint32_t numComponents = 0; + uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB); + uint32_t sampleCounter; + + for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) { + uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID); + if (sampleChannel != currentChannel) { + numComponents++; + currentChannel = sampleChannel; + } + } + return numComponents; +} + +/** + * @~English + * @brief Recreate the value of bytesPlane0 from sample info. + * + * This can be use to recreate the value of bytesPlane0 for data that + * has been variable-rate compressed so has bytesPlane0 = 0. For DFDs + * that are valid for KTX files. Little-endian data only and no multi-plane + * formats. + * + * @param DFD Pointer to a Data Format Descriptor for which, + * described as 32-bit words in native endianness. + * Note that this is the whole descriptor, not just + * the basic descriptor block. + * @param bytesPlane0 pointer to a 32-bit word in which the recreated + * value of bytesPlane0 will be written. + */ +void +recreateBytesPlane0FromSampleInfo(const uint32_t* DFD, uint32_t* bytesPlane0) +{ + const uint32_t *BDFDB = DFD+1; + uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB); + uint32_t sampleCounter; + + uint32_t bitsPlane0 = 0; + uint32_t* bitOffsets = malloc(sizeof(uint32_t) * numSamples); + memset(bitOffsets, -1, sizeof(uint32_t) * numSamples); + for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) { + uint32_t sampleBitOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET); + /* The sample bitLength field stores the bit length - 1. */ + uint32_t sampleBitLength = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1; + uint32_t i; + for (i = 0; i < numSamples; i++) { + if (sampleBitOffset == bitOffsets[i]) { + // This sample is being repeated as in e.g. RGB9E5. + break; + } + } + if (i == numSamples) { + // Previously unseen bitOffset. Bump size. + bitsPlane0 += sampleBitLength; + bitOffsets[sampleCounter] = sampleBitOffset; + } + } + free(bitOffsets); + *bytesPlane0 = bitsPlane0 >> 3U; +} + diff --git a/thirdparty/libktx/lib/dfdutils/vk2dfd.c b/thirdparty/libktx/lib/dfdutils/vk2dfd.c new file mode 100644 index 0000000000..d476ced1be --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/vk2dfd.c @@ -0,0 +1,34 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* Copyright 2019-2020 Mark Callow + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * @brief Create a DFD for a VkFormat. + */ + +#include "dfd.h" + +/** + * @~English + * @brief Create a DFD matching a VkFormat. + * + * @param[in] format VkFormat for which to create a DFD. + * + * @return pointer to the created DFD or 0 if format not supported or + * unrecognized. Caller is responsible for freeing the created + * DFD. + */ +uint32_t* +vk2dfd(enum VkFormat format) + { + switch (format) { +#include "vk2dfd.inl" + default: return 0; + } + } + diff --git a/thirdparty/libktx/lib/dfdutils/vk2dfd.inl b/thirdparty/libktx/lib/dfdutils/vk2dfd.inl new file mode 100644 index 0000000000..4ae1e6d5e1 --- /dev/null +++ b/thirdparty/libktx/lib/dfdutils/vk2dfd.inl @@ -0,0 +1,340 @@ +/* Copyright 2019-2020 The Khronos Group Inc. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/***************************** Do not edit. ***************************** + Automatically generated by makevk2dfd.pl. + *************************************************************************/ + +/* Vulkan combined depth & stencil formats are not included here + * because they do not exist outside a Vulkan device. + */ +case VK_FORMAT_R4G4_UNORM_PACK8: { + int channels[] = {1,0}; int bits[] = {4,4}; + return createDFDPacked(0, 2, bits, channels, s_UNORM); +} +case VK_FORMAT_R4G4B4A4_UNORM_PACK16: { + int channels[] = {3,2,1,0}; int bits[] = {4,4,4,4}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} +case VK_FORMAT_B4G4R4A4_UNORM_PACK16: { + int channels[] = {3,0,1,2}; int bits[] = {4,4,4,4}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} +case VK_FORMAT_R5G6B5_UNORM_PACK16: { + int channels[] = {2,1,0}; int bits[] = {5,6,5}; + return createDFDPacked(0, 3, bits, channels, s_UNORM); +} +case VK_FORMAT_B5G6R5_UNORM_PACK16: { + int channels[] = {0,1,2}; int bits[] = {5,6,5}; + return createDFDPacked(0, 3, bits, channels, s_UNORM); +} +case VK_FORMAT_R5G5B5A1_UNORM_PACK16: { + int channels[] = {3,2,1,0}; int bits[] = {1,5,5,5}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} +case VK_FORMAT_B5G5R5A1_UNORM_PACK16: { + int channels[] = {3,0,1,2}; int bits[] = {1,5,5,5}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} +case VK_FORMAT_A1R5G5B5_UNORM_PACK16: { + int channels[] = {2,1,0,3}; int bits[] = {5,5,5,1}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} +case VK_FORMAT_R8_UNORM: return createDFDUnpacked(0, 1, 1, 0, s_UNORM); +case VK_FORMAT_R8_SNORM: return createDFDUnpacked(0, 1, 1, 0, s_SNORM); +case VK_FORMAT_R8_USCALED: return createDFDUnpacked(0, 1, 1, 0, s_USCALED); +case VK_FORMAT_R8_SSCALED: return createDFDUnpacked(0, 1, 1, 0, s_SSCALED); +case VK_FORMAT_R8_UINT: return createDFDUnpacked(0, 1, 1, 0, s_UINT); +case VK_FORMAT_R8_SINT: return createDFDUnpacked(0, 1, 1, 0, s_SINT); +case VK_FORMAT_R8_SRGB: return createDFDUnpacked(0, 1, 1, 0, s_SRGB); +case VK_FORMAT_R8G8_UNORM: return createDFDUnpacked(0, 2, 1, 0, s_UNORM); +case VK_FORMAT_R8G8_SNORM: return createDFDUnpacked(0, 2, 1, 0, s_SNORM); +case VK_FORMAT_R8G8_USCALED: return createDFDUnpacked(0, 2, 1, 0, s_USCALED); +case VK_FORMAT_R8G8_SSCALED: return createDFDUnpacked(0, 2, 1, 0, s_SSCALED); +case VK_FORMAT_R8G8_UINT: return createDFDUnpacked(0, 2, 1, 0, s_UINT); +case VK_FORMAT_R8G8_SINT: return createDFDUnpacked(0, 2, 1, 0, s_SINT); +case VK_FORMAT_R8G8_SRGB: return createDFDUnpacked(0, 2, 1, 0, s_SRGB); +case VK_FORMAT_R8G8B8_UNORM: return createDFDUnpacked(0, 3, 1, 0, s_UNORM); +case VK_FORMAT_R8G8B8_SNORM: return createDFDUnpacked(0, 3, 1, 0, s_SNORM); +case VK_FORMAT_R8G8B8_USCALED: return createDFDUnpacked(0, 3, 1, 0, s_USCALED); +case VK_FORMAT_R8G8B8_SSCALED: return createDFDUnpacked(0, 3, 1, 0, s_SSCALED); +case VK_FORMAT_R8G8B8_UINT: return createDFDUnpacked(0, 3, 1, 0, s_UINT); +case VK_FORMAT_R8G8B8_SINT: return createDFDUnpacked(0, 3, 1, 0, s_SINT); +case VK_FORMAT_R8G8B8_SRGB: return createDFDUnpacked(0, 3, 1, 0, s_SRGB); +case VK_FORMAT_B8G8R8_UNORM: return createDFDUnpacked(0, 3, 1, 1, s_UNORM); +case VK_FORMAT_B8G8R8_SNORM: return createDFDUnpacked(0, 3, 1, 1, s_SNORM); +case VK_FORMAT_B8G8R8_USCALED: return createDFDUnpacked(0, 3, 1, 1, s_USCALED); +case VK_FORMAT_B8G8R8_SSCALED: return createDFDUnpacked(0, 3, 1, 1, s_SSCALED); +case VK_FORMAT_B8G8R8_UINT: return createDFDUnpacked(0, 3, 1, 1, s_UINT); +case VK_FORMAT_B8G8R8_SINT: return createDFDUnpacked(0, 3, 1, 1, s_SINT); +case VK_FORMAT_B8G8R8_SRGB: return createDFDUnpacked(0, 3, 1, 1, s_SRGB); +case VK_FORMAT_R8G8B8A8_UNORM: return createDFDUnpacked(0, 4, 1, 0, s_UNORM); +case VK_FORMAT_R8G8B8A8_SNORM: return createDFDUnpacked(0, 4, 1, 0, s_SNORM); +case VK_FORMAT_R8G8B8A8_USCALED: return createDFDUnpacked(0, 4, 1, 0, s_USCALED); +case VK_FORMAT_R8G8B8A8_SSCALED: return createDFDUnpacked(0, 4, 1, 0, s_SSCALED); +case VK_FORMAT_R8G8B8A8_UINT: return createDFDUnpacked(0, 4, 1, 0, s_UINT); +case VK_FORMAT_R8G8B8A8_SINT: return createDFDUnpacked(0, 4, 1, 0, s_SINT); +case VK_FORMAT_R8G8B8A8_SRGB: return createDFDUnpacked(0, 4, 1, 0, s_SRGB); +case VK_FORMAT_B8G8R8A8_UNORM: return createDFDUnpacked(0, 4, 1, 1, s_UNORM); +case VK_FORMAT_B8G8R8A8_SNORM: return createDFDUnpacked(0, 4, 1, 1, s_SNORM); +case VK_FORMAT_B8G8R8A8_USCALED: return createDFDUnpacked(0, 4, 1, 1, s_USCALED); +case VK_FORMAT_B8G8R8A8_SSCALED: return createDFDUnpacked(0, 4, 1, 1, s_SSCALED); +case VK_FORMAT_B8G8R8A8_UINT: return createDFDUnpacked(0, 4, 1, 1, s_UINT); +case VK_FORMAT_B8G8R8A8_SINT: return createDFDUnpacked(0, 4, 1, 1, s_SINT); +case VK_FORMAT_B8G8R8A8_SRGB: return createDFDUnpacked(0, 4, 1, 1, s_SRGB); +case VK_FORMAT_A8B8G8R8_UNORM_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} +case VK_FORMAT_A8B8G8R8_SNORM_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8}; + return createDFDPacked(0, 4, bits, channels, s_SNORM); +} +case VK_FORMAT_A8B8G8R8_USCALED_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8}; + return createDFDPacked(0, 4, bits, channels, s_USCALED); +} +case VK_FORMAT_A8B8G8R8_SSCALED_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8}; + return createDFDPacked(0, 4, bits, channels, s_SSCALED); +} +case VK_FORMAT_A8B8G8R8_UINT_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8}; + return createDFDPacked(0, 4, bits, channels, s_UINT); +} +case VK_FORMAT_A8B8G8R8_SINT_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8}; + return createDFDPacked(0, 4, bits, channels, s_SINT); +} +case VK_FORMAT_A8B8G8R8_SRGB_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8}; + return createDFDPacked(0, 4, bits, channels, s_SRGB); +} +case VK_FORMAT_A2R10G10B10_UNORM_PACK32: { + int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} +case VK_FORMAT_A2R10G10B10_SNORM_PACK32: { + int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_SNORM); +} +case VK_FORMAT_A2R10G10B10_USCALED_PACK32: { + int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_USCALED); +} +case VK_FORMAT_A2R10G10B10_SSCALED_PACK32: { + int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_SSCALED); +} +case VK_FORMAT_A2R10G10B10_UINT_PACK32: { + int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_UINT); +} +case VK_FORMAT_A2R10G10B10_SINT_PACK32: { + int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_SINT); +} +case VK_FORMAT_A2B10G10R10_UNORM_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} +case VK_FORMAT_A2B10G10R10_SNORM_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_SNORM); +} +case VK_FORMAT_A2B10G10R10_USCALED_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_USCALED); +} +case VK_FORMAT_A2B10G10R10_SSCALED_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_SSCALED); +} +case VK_FORMAT_A2B10G10R10_UINT_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_UINT); +} +case VK_FORMAT_A2B10G10R10_SINT_PACK32: { + int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2}; + return createDFDPacked(0, 4, bits, channels, s_SINT); +} +case VK_FORMAT_R16_UNORM: return createDFDUnpacked(0, 1, 2, 0, s_UNORM); +case VK_FORMAT_R16_SNORM: return createDFDUnpacked(0, 1, 2, 0, s_SNORM); +case VK_FORMAT_R16_USCALED: return createDFDUnpacked(0, 1, 2, 0, s_USCALED); +case VK_FORMAT_R16_SSCALED: return createDFDUnpacked(0, 1, 2, 0, s_SSCALED); +case VK_FORMAT_R16_UINT: return createDFDUnpacked(0, 1, 2, 0, s_UINT); +case VK_FORMAT_R16_SINT: return createDFDUnpacked(0, 1, 2, 0, s_SINT); +case VK_FORMAT_R16_SFLOAT: return createDFDUnpacked(0, 1, 2, 0, s_SFLOAT); +case VK_FORMAT_R16G16_UNORM: return createDFDUnpacked(0, 2, 2, 0, s_UNORM); +case VK_FORMAT_R16G16_SNORM: return createDFDUnpacked(0, 2, 2, 0, s_SNORM); +case VK_FORMAT_R16G16_USCALED: return createDFDUnpacked(0, 2, 2, 0, s_USCALED); +case VK_FORMAT_R16G16_SSCALED: return createDFDUnpacked(0, 2, 2, 0, s_SSCALED); +case VK_FORMAT_R16G16_UINT: return createDFDUnpacked(0, 2, 2, 0, s_UINT); +case VK_FORMAT_R16G16_SINT: return createDFDUnpacked(0, 2, 2, 0, s_SINT); +case VK_FORMAT_R16G16_SFLOAT: return createDFDUnpacked(0, 2, 2, 0, s_SFLOAT); +case VK_FORMAT_R16G16B16_UNORM: return createDFDUnpacked(0, 3, 2, 0, s_UNORM); +case VK_FORMAT_R16G16B16_SNORM: return createDFDUnpacked(0, 3, 2, 0, s_SNORM); +case VK_FORMAT_R16G16B16_USCALED: return createDFDUnpacked(0, 3, 2, 0, s_USCALED); +case VK_FORMAT_R16G16B16_SSCALED: return createDFDUnpacked(0, 3, 2, 0, s_SSCALED); +case VK_FORMAT_R16G16B16_UINT: return createDFDUnpacked(0, 3, 2, 0, s_UINT); +case VK_FORMAT_R16G16B16_SINT: return createDFDUnpacked(0, 3, 2, 0, s_SINT); +case VK_FORMAT_R16G16B16_SFLOAT: return createDFDUnpacked(0, 3, 2, 0, s_SFLOAT); +case VK_FORMAT_R16G16B16A16_UNORM: return createDFDUnpacked(0, 4, 2, 0, s_UNORM); +case VK_FORMAT_R16G16B16A16_SNORM: return createDFDUnpacked(0, 4, 2, 0, s_SNORM); +case VK_FORMAT_R16G16B16A16_USCALED: return createDFDUnpacked(0, 4, 2, 0, s_USCALED); +case VK_FORMAT_R16G16B16A16_SSCALED: return createDFDUnpacked(0, 4, 2, 0, s_SSCALED); +case VK_FORMAT_R16G16B16A16_UINT: return createDFDUnpacked(0, 4, 2, 0, s_UINT); +case VK_FORMAT_R16G16B16A16_SINT: return createDFDUnpacked(0, 4, 2, 0, s_SINT); +case VK_FORMAT_R16G16B16A16_SFLOAT: return createDFDUnpacked(0, 4, 2, 0, s_SFLOAT); +case VK_FORMAT_R32_UINT: return createDFDUnpacked(0, 1, 4, 0, s_UINT); +case VK_FORMAT_R32_SINT: return createDFDUnpacked(0, 1, 4, 0, s_SINT); +case VK_FORMAT_R32_SFLOAT: return createDFDUnpacked(0, 1, 4, 0, s_SFLOAT); +case VK_FORMAT_R32G32_UINT: return createDFDUnpacked(0, 2, 4, 0, s_UINT); +case VK_FORMAT_R32G32_SINT: return createDFDUnpacked(0, 2, 4, 0, s_SINT); +case VK_FORMAT_R32G32_SFLOAT: return createDFDUnpacked(0, 2, 4, 0, s_SFLOAT); +case VK_FORMAT_R32G32B32_UINT: return createDFDUnpacked(0, 3, 4, 0, s_UINT); +case VK_FORMAT_R32G32B32_SINT: return createDFDUnpacked(0, 3, 4, 0, s_SINT); +case VK_FORMAT_R32G32B32_SFLOAT: return createDFDUnpacked(0, 3, 4, 0, s_SFLOAT); +case VK_FORMAT_R32G32B32A32_UINT: return createDFDUnpacked(0, 4, 4, 0, s_UINT); +case VK_FORMAT_R32G32B32A32_SINT: return createDFDUnpacked(0, 4, 4, 0, s_SINT); +case VK_FORMAT_R32G32B32A32_SFLOAT: return createDFDUnpacked(0, 4, 4, 0, s_SFLOAT); +case VK_FORMAT_R64_UINT: return createDFDUnpacked(0, 1, 8, 0, s_UINT); +case VK_FORMAT_R64_SINT: return createDFDUnpacked(0, 1, 8, 0, s_SINT); +case VK_FORMAT_R64_SFLOAT: return createDFDUnpacked(0, 1, 8, 0, s_SFLOAT); +case VK_FORMAT_R64G64_UINT: return createDFDUnpacked(0, 2, 8, 0, s_UINT); +case VK_FORMAT_R64G64_SINT: return createDFDUnpacked(0, 2, 8, 0, s_SINT); +case VK_FORMAT_R64G64_SFLOAT: return createDFDUnpacked(0, 2, 8, 0, s_SFLOAT); +case VK_FORMAT_R64G64B64_UINT: return createDFDUnpacked(0, 3, 8, 0, s_UINT); +case VK_FORMAT_R64G64B64_SINT: return createDFDUnpacked(0, 3, 8, 0, s_SINT); +case VK_FORMAT_R64G64B64_SFLOAT: return createDFDUnpacked(0, 3, 8, 0, s_SFLOAT); +case VK_FORMAT_R64G64B64A64_UINT: return createDFDUnpacked(0, 4, 8, 0, s_UINT); +case VK_FORMAT_R64G64B64A64_SINT: return createDFDUnpacked(0, 4, 8, 0, s_SINT); +case VK_FORMAT_R64G64B64A64_SFLOAT: return createDFDUnpacked(0, 4, 8, 0, s_SFLOAT); +case VK_FORMAT_B10G11R11_UFLOAT_PACK32: { + int channels[] = {0,1,2}; int bits[] = {11,11,10}; + return createDFDPacked(0, 3, bits, channels, s_UFLOAT); +} +case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: { + int bits[] = {0}; int channels[] = {0}; + return createDFDPacked(0, 6, bits, channels, s_UFLOAT); +} +case VK_FORMAT_D16_UNORM: return createDFDDepthStencil(16,0,2); +case VK_FORMAT_X8_D24_UNORM_PACK32: return createDFDDepthStencil(24,0,4); +case VK_FORMAT_D32_SFLOAT: return createDFDDepthStencil(32,0,4); +case VK_FORMAT_S8_UINT: return createDFDDepthStencil(0,8,1); +case VK_FORMAT_BC1_RGB_UNORM_BLOCK: return createDFDCompressed(c_BC1_RGB, 4, 4, 1, s_UNORM); +case VK_FORMAT_BC1_RGB_SRGB_BLOCK: return createDFDCompressed(c_BC1_RGB, 4, 4, 1, s_SRGB); +case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: return createDFDCompressed(c_BC1_RGBA, 4, 4, 1, s_UNORM); +case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: return createDFDCompressed(c_BC1_RGBA, 4, 4, 1, s_SRGB); +case VK_FORMAT_BC2_UNORM_BLOCK: return createDFDCompressed(c_BC2, 4, 4, 1, s_UNORM); +case VK_FORMAT_BC2_SRGB_BLOCK: return createDFDCompressed(c_BC2, 4, 4, 1, s_SRGB); +case VK_FORMAT_BC3_UNORM_BLOCK: return createDFDCompressed(c_BC3, 4, 4, 1, s_UNORM); +case VK_FORMAT_BC3_SRGB_BLOCK: return createDFDCompressed(c_BC3, 4, 4, 1, s_SRGB); +case VK_FORMAT_BC4_UNORM_BLOCK: return createDFDCompressed(c_BC4, 4, 4, 1, s_UNORM); +case VK_FORMAT_BC4_SNORM_BLOCK: return createDFDCompressed(c_BC4, 4, 4, 1, s_SNORM); +case VK_FORMAT_BC5_UNORM_BLOCK: return createDFDCompressed(c_BC5, 4, 4, 1, s_UNORM); +case VK_FORMAT_BC5_SNORM_BLOCK: return createDFDCompressed(c_BC5, 4, 4, 1, s_SNORM); +case VK_FORMAT_BC6H_UFLOAT_BLOCK: return createDFDCompressed(c_BC6H, 4, 4, 1, s_UFLOAT); +case VK_FORMAT_BC6H_SFLOAT_BLOCK: return createDFDCompressed(c_BC6H, 4, 4, 1, s_SFLOAT); +case VK_FORMAT_BC7_UNORM_BLOCK: return createDFDCompressed(c_BC7, 4, 4, 1, s_UNORM); +case VK_FORMAT_BC7_SRGB_BLOCK: return createDFDCompressed(c_BC7, 4, 4, 1, s_SRGB); +case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8, 4, 4, 1, s_UNORM); +case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8, 4, 4, 1, s_SRGB); +case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8A1, 4, 4, 1, s_UNORM); +case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8A1, 4, 4, 1, s_SRGB); +case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8A8, 4, 4, 1, s_UNORM); +case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8A8, 4, 4, 1, s_SRGB); +case VK_FORMAT_EAC_R11_UNORM_BLOCK: return createDFDCompressed(c_EAC_R11, 4, 4, 1, s_UNORM); +case VK_FORMAT_EAC_R11_SNORM_BLOCK: return createDFDCompressed(c_EAC_R11, 4, 4, 1, s_SNORM); +case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: return createDFDCompressed(c_EAC_R11G11, 4, 4, 1, s_UNORM); +case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: return createDFDCompressed(c_EAC_R11G11, 4, 4, 1, s_SNORM); +case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 4, 4, 1, s_UNORM); +case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 4, 4, 1, s_SRGB); +case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 5, 4, 1, s_UNORM); +case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 5, 4, 1, s_SRGB); +case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 5, 5, 1, s_UNORM); +case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 5, 5, 1, s_SRGB); +case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 6, 5, 1, s_UNORM); +case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 6, 5, 1, s_SRGB); +case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 6, 6, 1, s_UNORM); +case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 6, 6, 1, s_SRGB); +case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 8, 5, 1, s_UNORM); +case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 8, 5, 1, s_SRGB); +case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 8, 6, 1, s_UNORM); +case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 8, 6, 1, s_SRGB); +case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 8, 8, 1, s_UNORM); +case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 8, 8, 1, s_SRGB); +case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 10, 5, 1, s_UNORM); +case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 10, 5, 1, s_SRGB); +case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 10, 6, 1, s_UNORM); +case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 10, 6, 1, s_SRGB); +case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 10, 8, 1, s_UNORM); +case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 10, 8, 1, s_SRGB); +case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 10, 10, 1, s_UNORM); +case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 10, 10, 1, s_SRGB); +case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 12, 10, 1, s_UNORM); +case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 12, 10, 1, s_SRGB); +case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 12, 12, 1, s_UNORM); +case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 12, 12, 1, s_SRGB); +case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 8, 4, 1, s_UNORM); +case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 4, 4, 1, s_UNORM); +case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC2, 8, 4, 1, s_UNORM); +case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC2, 4, 4, 1, s_UNORM); +case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 8, 4, 1, s_SRGB); +case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 4, 4, 1, s_SRGB); +case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: return createDFDCompressed(c_PVRTC2, 8, 4, 1, s_SRGB); +case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: return createDFDCompressed(c_PVRTC2, 4, 4, 1, s_SRGB); +case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 1, s_SFLOAT); +case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 4, 1, s_SFLOAT); +case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 1, s_SFLOAT); +case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 5, 1, s_SFLOAT); +case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 1, s_SFLOAT); +case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 8, 5, 1, s_SFLOAT); +case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 8, 6, 1, s_SFLOAT); +case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 8, 8, 1, s_SFLOAT); +case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 5, 1, s_SFLOAT); +case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 6, 1, s_SFLOAT); +case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 8, 1, s_SFLOAT); +case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 10, 1, s_SFLOAT); +case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 12, 10, 1, s_SFLOAT); +case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 12, 12, 1, s_SFLOAT); +#if 0 +case VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_UNORM); +case VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_SRGB); +case VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_SFLOAT); +case VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 3, 3, s_UNORM); +case VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 3, 3, s_SRGB); +case VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 3, 3, s_SFLOAT); +case VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 3, s_UNORM); +case VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 3, s_SRGB); +case VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 3, s_SFLOAT); +case VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 4, s_UNORM); +case VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 4, s_SRGB); +case VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 4, s_SFLOAT); +case VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 4, 4, s_UNORM); +case VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 4, 4, s_SRGB); +case VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 4, 4, s_SFLOAT); +case VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 4, s_UNORM); +case VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 4, s_SRGB); +case VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 4, s_SFLOAT); +case VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 5, s_UNORM); +case VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 5, s_SRGB); +case VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 5, s_SFLOAT); +case VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 5, 5, s_UNORM); +case VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 5, 5, s_SRGB); +case VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 5, 5, s_SFLOAT); +case VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 5, s_UNORM); +case VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 5, s_SRGB); +case VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 5, s_SFLOAT); +case VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_UNORM); +case VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_SRGB); +case VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_SFLOAT); +#endif +case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT: { + int channels[] = {2,1,0,3}; int bits[] = {4,4,4,4}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} +case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT: { + int channels[] = {0,1,2,3}; int bits[] = {4,4,4,4}; + return createDFDPacked(0, 4, bits, channels, s_UNORM); +} diff --git a/thirdparty/libktx/lib/filestream.c b/thirdparty/libktx/lib/filestream.c new file mode 100644 index 0000000000..b1e0eba7c6 --- /dev/null +++ b/thirdparty/libktx/lib/filestream.c @@ -0,0 +1,393 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2010-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * + * @brief Implementation of ktxStream for FILE. + * + * @author Maksim Kolesin, Under Development + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <string.h> +/* I need these on Linux. Why? */ +#define __USE_LARGEFILE 1 // For declaration of ftello, etc. +#define __USE_POSIX 1 // For declaration of fileno. +#define _POSIX_SOURCE 1 // For both the above in Emscripten. +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> // For stat.h on Windows +#define __USE_MISC 1 // For declaration of S_IF... +#include <sys/stat.h> + +#include "ktx.h" +#include "ktxint.h" +#include "filestream.h" + +// Gotta love Windows :-( +#if defined(_MSC_VER) + #if defined(_WIN64) + #define ftello _ftelli64 + #define fseeko _fseeki64 + #else + #define ftello ftell + #define fseeko fseek + #endif + #define fileno _fileno + #define fstat _fstat + #define stat _stat + #define S_IFIFO _S_IFIFO + #define S_IFSOCK 0xC000 + typedef unsigned short mode_t; +#endif + +#if defined(__MINGW32__) + #define S_IFSOCK 0xC000 +#endif + +#define KTX_FILE_STREAM_MAX (1 << (sizeof(ktx_off_t) - 1) - 1) + +/** + * @~English + * @brief Read bytes from a ktxFileStream. + * + * @param [in] str pointer to the ktxStream from which to read. + * @param [out] dst pointer to a block of memory with a size + * of at least @p size bytes, converted to a void*. + * @param [in,out] count pointer to total count of bytes to be read. + * On completion set to number of bytes read. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p src is @c NULL. + * @exception KTX_FILE_READ_ERROR an error occurred while reading the file. + * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request. + */ +static +KTX_error_code ktxFileStream_read(ktxStream* str, void* dst, const ktx_size_t count) +{ + ktx_size_t nread; + + if (!str || !dst) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeFile); + + if ((nread = fread(dst, 1, count, str->data.file)) != count) { + if (feof(str->data.file)) { + return KTX_FILE_UNEXPECTED_EOF; + } else { + return KTX_FILE_READ_ERROR; + } + } + str->readpos += count; + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Skip bytes in a ktxFileStream. + * + * @param [in] str pointer to a ktxStream object. + * @param [in] count number of bytes to be skipped. + * + * In order to support applications reading from stdin, read characters + * rather than using seek functions. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str is @c NULL or @p count is less than zero. + * @exception KTX_INVALID_OPERATION skipping @p count bytes would go beyond EOF. + * @exception KTX_FILE_READ_ERROR an error occurred while reading the file. + * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request. + * @p count is set to the number of bytes + * skipped. + */ +static +KTX_error_code ktxFileStream_skip(ktxStream* str, const ktx_size_t count) +{ + if (!str) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeFile); + + for (ktx_uint32_t i = 0; i < count; i++) { + int ret = getc(str->data.file); + if (ret == EOF) { + if (feof(str->data.file)) { + return KTX_FILE_UNEXPECTED_EOF; + } else { + return KTX_FILE_READ_ERROR; + } + } + } + str->readpos += count; + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Write bytes to a ktxFileStream. + * + * @param [in] str pointer to the ktxStream that is the destination of the + * write. + * @param [in] src pointer to the array of elements to be written, + * converted to a const void*. + * @param [in] size size in bytes of each element to be written. + * @param [in] count number of elements, each one with a @p size of size + * bytes. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str is @c NULL or @p src is @c NULL. + * @exception KTX_FILE_OVERFLOW the requested write would caused the file to + * exceed the maximum supported file size. + * @exception KTX_FILE_WRITE_ERROR a system error occurred while writing the + * file. + */ +static +KTX_error_code ktxFileStream_write(ktxStream* str, const void *src, + const ktx_size_t size, + const ktx_size_t count) +{ + if (!str || !src) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeFile); + + if (fwrite(src, size, count, str->data.file) != count) { + if (errno == EFBIG || errno == EOVERFLOW) + return KTX_FILE_OVERFLOW; + else + return KTX_FILE_WRITE_ERROR; + } + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Get the current read/write position in a ktxFileStream. + * + * @param [in] str pointer to the ktxStream to query. + * @param [in,out] off pointer to variable to receive the offset value. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_ISPIPE file descriptor underlying stream is associated + * with a pipe or FIFO so does not have a + * file-position indicator. + * @exception KTX_INVALID_VALUE @p str or @p pos is @c NULL. + */ +static +KTX_error_code ktxFileStream_getpos(ktxStream* str, ktx_off_t* pos) +{ + ktx_off_t ftellval; + + if (!str || !pos) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeFile); + + if (str->data.file == stdin) { + *pos = str->readpos; + } else { + /* The cast quiets an Xcode warning when building for "Generic iOS Device". + * I'm not sure why. + */ + ftellval = (ktx_off_t)ftello(str->data.file); + if (ftellval < 0) { + switch (errno) { + case ESPIPE: return KTX_FILE_ISPIPE; + case EOVERFLOW: return KTX_FILE_OVERFLOW; + } + } + + *pos = ftellval; + } + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Set the current read/write position in a ktxFileStream. + * + * Offset of 0 is the start of the file. This function operates + * like Linux > 3.1's @c lseek() when it is passed a @c whence + * of @c SEEK_DATA as it returns an error if the seek would + * go beyond the end of the file. + * + * @param [in] str pointer to the ktxStream whose r/w position is to be set. + * @param [in] off pointer to the offset value to set. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * Throws the same exceptions as ktxFileStream_getsize() for the reasons given + * there plus the following: + * + * @exception KTX_INVALID_VALUE @p str is @c NULL. + * @exception KTX_INVALID_OPERATION @p pos is > the size of the file or an + * fseek error occurred. + */ +static +KTX_error_code ktxFileStream_setpos(ktxStream* str, ktx_off_t pos) +{ + ktx_size_t fileSize; + KTX_error_code result; + + if (!str) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeFile); + + if (str->data.file == stdin) { + if (pos > str->readpos) + return str->skip(str, pos - str->readpos); + else + return KTX_FILE_ISPIPE; + } + + result = str->getsize(str, &fileSize); + + if (result != KTX_SUCCESS) { + // Device is likely not seekable. + return result; + } + + if (pos > (ktx_off_t)fileSize) + return KTX_INVALID_OPERATION; + + if (fseeko(str->data.file, pos, SEEK_SET) < 0) + return KTX_FILE_SEEK_ERROR; + else + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Get the size of a ktxFileStream in bytes. + * + * @param [in] str pointer to the ktxStream whose size is to be queried. + * @param [in,out] size pointer to a variable in which size will be written. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_OVERFLOW size is too large to be returned in a + * @c ktx_size_t. + * @exception KTX_FILE_ISPIPE file descriptor underlying stream is associated + * with a pipe or FIFO so does not have a + * file-position indicator. + * @exception KTX_FILE_READ_ERROR a system error occurred while getting the + * size. + * @exception KTX_INVALID_VALUE @p str or @p size is @c NULL. + * @exception KTX_INVALID_OPERATION stream is a tty. + */ +static +KTX_error_code ktxFileStream_getsize(ktxStream* str, ktx_size_t* size) +{ + struct stat statbuf; + int statret; + + if (!str || !size) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeFile); + + // Need to flush so that fstat will return the current size. + // Can ignore return value. The only error that can happen is to tell you + // it was a NOP because the file is read only. +#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(__MINGW64__) && !defined(_UCRT) + // Bug in VS2013 msvcrt. fflush on FILE open for READ changes file offset + // to 4096. + if (str->data.file->_flag & _IOWRT) +#endif + (void)fflush(str->data.file); + statret = fstat(fileno(str->data.file), &statbuf); + if (statret < 0) { + switch (errno) { + case EOVERFLOW: return KTX_FILE_OVERFLOW; + case EIO: + default: + return KTX_FILE_READ_ERROR; + } + } + + mode_t ftype = statbuf.st_mode & S_IFMT; + if (ftype == S_IFIFO || ftype == S_IFSOCK) + return KTX_FILE_ISPIPE; + + if (statbuf.st_mode & S_IFCHR) + return KTX_INVALID_OPERATION; + + *size = (ktx_size_t)statbuf.st_size; /* See _getpos for why this cast. */ + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Initialize a ktxFileStream. + * + * @param [in] str pointer to the ktxStream to initialize. + * @param [in] file pointer to the underlying FILE object. + * @param [in] closeFileOnDestruct if not false, stdio file pointer will be closed when ktxStream + * is destructed. + * + * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error. + * + * @exception KTX_INVALID_VALUE @p stream is @c NULL or @p file is @c NULL. + */ +KTX_error_code ktxFileStream_construct(ktxStream* str, FILE* file, + ktx_bool_t closeFileOnDestruct) +{ + if (!str || !file) + return KTX_INVALID_VALUE; + + str->data.file = file; + str->readpos = 0; + str->type = eStreamTypeFile; + str->read = ktxFileStream_read; + str->skip = ktxFileStream_skip; + str->write = ktxFileStream_write; + str->getpos = ktxFileStream_getpos; + str->setpos = ktxFileStream_setpos; + str->getsize = ktxFileStream_getsize; + str->destruct = ktxFileStream_destruct; + str->closeOnDestruct = closeFileOnDestruct; + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Destruct the stream, potentially closing the underlying FILE. + * + * This only closes the underyling FILE if the @c closeOnDestruct parameter to + * ktxFileStream_construct() was not @c KTX_FALSE. + * + * @param [in] str pointer to the ktxStream whose FILE is to potentially + * be closed. + */ +void +ktxFileStream_destruct(ktxStream* str) +{ + assert(str && str->type == eStreamTypeFile); + + if (str->closeOnDestruct) + fclose(str->data.file); + str->data.file = 0; +} diff --git a/thirdparty/libktx/lib/filestream.h b/thirdparty/libktx/lib/filestream.h new file mode 100644 index 0000000000..5c0ea7d2dd --- /dev/null +++ b/thirdparty/libktx/lib/filestream.h @@ -0,0 +1,27 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2010-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Author: Maksim Kolesin from original code + * by Mark Callow and Georg Kolling + */ + +#ifndef FILESTREAM_H +#define FILESTREAM_H + +#include "ktx.h" + +/* + * ktxFileInit: Initialize a ktxStream to a ktxFileStream with a FILE object + */ +KTX_error_code ktxFileStream_construct(ktxStream* str, FILE* file, + ktx_bool_t closeFileOnDestruct); + +void ktxFileStream_destruct(ktxStream* str); + +#endif /* FILESTREAM_H */ diff --git a/thirdparty/libktx/lib/formatsize.h b/thirdparty/libktx/lib/formatsize.h new file mode 100644 index 0000000000..7112a3a90d --- /dev/null +++ b/thirdparty/libktx/lib/formatsize.h @@ -0,0 +1,58 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file + * @~English + * + * @brief Struct for returning size information about an image format. + * + * @author Mark Callow, www.edgewise-consulting.com + */ + +#ifndef _FORMATSIZE_H_ +#define _FORMATSIZE_H_ + +#include "ktx.h" + +typedef enum ktxFormatSizeFlagBits { + KTX_FORMAT_SIZE_PACKED_BIT = 0x00000001, + KTX_FORMAT_SIZE_COMPRESSED_BIT = 0x00000002, + KTX_FORMAT_SIZE_PALETTIZED_BIT = 0x00000004, + KTX_FORMAT_SIZE_DEPTH_BIT = 0x00000008, + KTX_FORMAT_SIZE_STENCIL_BIT = 0x00000010, +} ktxFormatSizeFlagBits; + +typedef ktx_uint32_t ktxFormatSizeFlags; + +/** + * @brief Structure for holding size information for a texture format. + */ +typedef struct ktxFormatSize { + ktxFormatSizeFlags flags; + unsigned int paletteSizeInBits; // For KTX1. + unsigned int blockSizeInBits; + unsigned int blockWidth; // in texels + unsigned int blockHeight; // in texels + unsigned int blockDepth; // in texels + unsigned int minBlocksX; // Minimum required number of blocks + unsigned int minBlocksY; +} ktxFormatSize; + +#ifdef __cplusplus +extern "C" { +#endif + +bool ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _FORMATSIZE_H_ */ diff --git a/thirdparty/libktx/lib/gl_format.h b/thirdparty/libktx/lib/gl_format.h new file mode 100644 index 0000000000..2381505a68 --- /dev/null +++ b/thirdparty/libktx/lib/gl_format.h @@ -0,0 +1,2654 @@ +/* +================================================================================================ + +Description : OpenGL formats/types and properties. +Author : J.M.P. van Waveren +Date : 07/17/2016 +Language : C99 +Format : Real tabs with the tab size equal to 4 spaces. +Copyright : Copyright (c) 2016 Oculus VR, LLC. All Rights reserved. + + +LICENSE +======= + +Copyright 2016 Oculus VR, LLC. +SPDX-License-Identifier: Apache-2.0 + + +DESCRIPTION +=========== + +This header stores the OpenGL formats/types and two simple routines +to derive the format/type from an internal format. These routines +are useful to verify the data in a KTX container files. The OpenGL +constants are generally useful to convert files like KTX and glTF +to different graphics APIs. + +This header stores the OpenGL formats/types that are used as parameters +to the following OpenGL functions: + +void glTexImage2D( GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, const GLvoid * data ); +void glTexImage3D( GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLenum format, GLenum type, const GLvoid * data ); +void glCompressedTexImage2D( GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid * data ); +void glCompressedTexImage3D( GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLsizei imageSize, const GLvoid * data ); +void glTexStorage2D( GLenum target, GLsizei levels, GLenum internalformat, + GLsizei width, GLsizei height ); +void glTexStorage3D( GLenum target, GLsizei levels, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth ); +void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid * pointer); + + +IMPLEMENTATION +============== + +This file does not include OpenGL / OpenGL ES headers because: + + 1. Including OpenGL / OpenGL ES headers is platform dependent and + may require a separate installation of an OpenGL SDK. + 2. The OpenGL format/type constants are the same between extensions and core. + 3. The OpenGL format/type constants are the same between OpenGL and OpenGL ES. + 4. The OpenGL constants in this header are also used to derive Vulkan formats + from the OpenGL formats/types stored in files like KTX and glTF. These file + formats may use OpenGL formats/types that are not supported by the OpenGL + implementation on the platform but are supported by the Vulkan implementation. + + +ENTRY POINTS +============ + +static inline GLenum glGetFormatFromInternalFormat( const GLenum internalFormat ); +static inline GLenum glGetTypeFromInternalFormat( const GLenum internalFormat ); +static inline void glGetFormatSize( const GLenum internalFormat, GlFormatSize * pFormatSize ); +static inline unsigned int glGetTypeSizeFromType( const GLenum type ); +static inline GLenum glGetInternalFormatFromVkFormat ( VkFormat format ); + +MODIFICATIONS for use in libktx +=============================== + +2018.3.23 Added glGetTypeSizeFromType. Mark Callow, Edgewise Consulting. +2019.3.09 #if 0 around GL type declarations. 〃 +2019.5.30 Use common ktxFormatSize to return results. 〃 +2019.5.30 Return blockSizeInBits 0 for default case of glGetFormatSize. 〃 +2019.5.30 Added glGetInternalFormatFromVkFormat. 〃 + +================================================================================================ +*/ + +#if !defined( GL_FORMAT_H ) +#define GL_FORMAT_H + +#include <assert.h> +#include "formatsize.h" +#include "vkformat_enum.h" + +#if defined(_WIN32) && !defined(__MINGW32__) +#ifndef NOMINMAX +#define NOMINMAX +#endif +#ifndef __cplusplus +#undef inline +#define inline __inline +#endif // __cplusplus +#endif + +/* +=========================================================================== +Avoid warnings or even errors when using strict C99. "Redefinition of +(type) is a C11 feature." All includers in libktx also include ktx.h where +they are also defined. +=========================================================================== +*/ +#if 0 +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLuint; +#endif + +#if !defined( GL_INVALID_VALUE ) +#define GL_INVALID_VALUE 0x0501 +#endif + +/* +================================================================================================================================ + +Format to glTexImage2D and glTexImage3D. + +================================================================================================================================ +*/ + +#if !defined( GL_RED ) +#define GL_RED 0x1903 // same as GL_RED_EXT +#endif +#if !defined( GL_GREEN ) +#define GL_GREEN 0x1904 // deprecated +#endif +#if !defined( GL_BLUE ) +#define GL_BLUE 0x1905 // deprecated +#endif +#if !defined( GL_ALPHA ) +#define GL_ALPHA 0x1906 // deprecated +#endif +#if !defined( GL_LUMINANCE ) +#define GL_LUMINANCE 0x1909 // deprecated +#endif +#if !defined( GL_SLUMINANCE ) +#define GL_SLUMINANCE 0x8C46 // deprecated, same as GL_SLUMINANCE_EXT +#endif +#if !defined( GL_LUMINANCE_ALPHA ) +#define GL_LUMINANCE_ALPHA 0x190A // deprecated +#endif +#if !defined( GL_SLUMINANCE_ALPHA ) +#define GL_SLUMINANCE_ALPHA 0x8C44 // deprecated, same as GL_SLUMINANCE_ALPHA_EXT +#endif +#if !defined( GL_INTENSITY ) +#define GL_INTENSITY 0x8049 // deprecated, same as GL_INTENSITY_EXT +#endif +#if !defined( GL_RG ) +#define GL_RG 0x8227 // same as GL_RG_EXT +#endif +#if !defined( GL_RGB ) +#define GL_RGB 0x1907 +#endif +#if !defined( GL_BGR ) +#define GL_BGR 0x80E0 // same as GL_BGR_EXT +#endif +#if !defined( GL_RGBA ) +#define GL_RGBA 0x1908 +#endif +#if !defined( GL_BGRA ) +#define GL_BGRA 0x80E1 // same as GL_BGRA_EXT +#endif +#if !defined( GL_RED_INTEGER ) +#define GL_RED_INTEGER 0x8D94 // same as GL_RED_INTEGER_EXT +#endif +#if !defined( GL_GREEN_INTEGER ) +#define GL_GREEN_INTEGER 0x8D95 // deprecated, same as GL_GREEN_INTEGER_EXT +#endif +#if !defined( GL_BLUE_INTEGER ) +#define GL_BLUE_INTEGER 0x8D96 // deprecated, same as GL_BLUE_INTEGER_EXT +#endif +#if !defined( GL_ALPHA_INTEGER ) +#define GL_ALPHA_INTEGER 0x8D97 // deprecated, same as GL_ALPHA_INTEGER_EXT +#endif +#if !defined( GL_LUMINANCE_INTEGER ) +#define GL_LUMINANCE_INTEGER 0x8D9C // deprecated, same as GL_LUMINANCE_INTEGER_EXT +#endif +#if !defined( GL_LUMINANCE_ALPHA_INTEGER ) +#define GL_LUMINANCE_ALPHA_INTEGER 0x8D9D // deprecated, same as GL_LUMINANCE_ALPHA_INTEGER_EXT +#endif +#if !defined( GL_RG_INTEGER ) +#define GL_RG_INTEGER 0x8228 // same as GL_RG_INTEGER_EXT +#endif +#if !defined( GL_RGB_INTEGER ) +#define GL_RGB_INTEGER 0x8D98 // same as GL_RGB_INTEGER_EXT +#endif +#if !defined( GL_BGR_INTEGER ) +#define GL_BGR_INTEGER 0x8D9A // same as GL_BGR_INTEGER_EXT +#endif +#if !defined( GL_RGBA_INTEGER ) +#define GL_RGBA_INTEGER 0x8D99 // same as GL_RGBA_INTEGER_EXT +#endif +#if !defined( GL_BGRA_INTEGER ) +#define GL_BGRA_INTEGER 0x8D9B // same as GL_BGRA_INTEGER_EXT +#endif +#if !defined( GL_COLOR_INDEX ) +#define GL_COLOR_INDEX 0x1900 // deprecated +#endif +#if !defined( GL_STENCIL_INDEX ) +#define GL_STENCIL_INDEX 0x1901 +#endif +#if !defined( GL_DEPTH_COMPONENT ) +#define GL_DEPTH_COMPONENT 0x1902 +#endif +#if !defined( GL_DEPTH_STENCIL ) +#define GL_DEPTH_STENCIL 0x84F9 // same as GL_DEPTH_STENCIL_NV and GL_DEPTH_STENCIL_EXT and GL_DEPTH_STENCIL_OES +#endif + +/* +================================================================================================================================ + +Type to glTexImage2D, glTexImage3D and glVertexAttribPointer. + +================================================================================================================================ +*/ + +#if !defined( GL_BYTE ) +#define GL_BYTE 0x1400 +#endif +#if !defined( GL_UNSIGNED_BYTE ) +#define GL_UNSIGNED_BYTE 0x1401 +#endif +#if !defined( GL_SHORT ) +#define GL_SHORT 0x1402 +#endif +#if !defined( GL_UNSIGNED_SHORT ) +#define GL_UNSIGNED_SHORT 0x1403 +#endif +#if !defined( GL_INT ) +#define GL_INT 0x1404 +#endif +#if !defined( GL_UNSIGNED_INT ) +#define GL_UNSIGNED_INT 0x1405 +#endif +#if !defined( GL_INT64 ) +#define GL_INT64 0x140E // same as GL_INT64_NV and GL_INT64_ARB +#endif +#if !defined( GL_UNSIGNED_INT64 ) +#define GL_UNSIGNED_INT64 0x140F // same as GL_UNSIGNED_INT64_NV and GL_UNSIGNED_INT64_ARB +#endif +#if !defined( GL_HALF_FLOAT ) +#define GL_HALF_FLOAT 0x140B // same as GL_HALF_FLOAT_NV and GL_HALF_FLOAT_ARB +#endif +#if !defined( GL_HALF_FLOAT_OES ) +#define GL_HALF_FLOAT_OES 0x8D61 // Note that this different from GL_HALF_FLOAT. +#endif +#if !defined( GL_FLOAT ) +#define GL_FLOAT 0x1406 +#endif +#if !defined( GL_DOUBLE ) +#define GL_DOUBLE 0x140A // same as GL_DOUBLE_EXT +#endif +#if !defined( GL_UNSIGNED_BYTE_3_3_2 ) +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 // same as GL_UNSIGNED_BYTE_3_3_2_EXT +#endif +#if !defined( GL_UNSIGNED_BYTE_2_3_3_REV ) +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 // same as GL_UNSIGNED_BYTE_2_3_3_REV_EXT +#endif +#if !defined( GL_UNSIGNED_SHORT_5_6_5 ) +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 // same as GL_UNSIGNED_SHORT_5_6_5_EXT +#endif +#if !defined( GL_UNSIGNED_SHORT_5_6_5_REV ) +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 // same as GL_UNSIGNED_SHORT_5_6_5_REV_EXT +#endif +#if !defined( GL_UNSIGNED_SHORT_4_4_4_4 ) +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 // same as GL_UNSIGNED_SHORT_4_4_4_4_EXT +#endif +#if !defined( GL_UNSIGNED_SHORT_4_4_4_4_REV ) +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 // same as GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG and GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT +#endif +#if !defined( GL_UNSIGNED_SHORT_5_5_5_1 ) +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 // same as GL_UNSIGNED_SHORT_5_5_5_1_EXT +#endif +#if !defined( GL_UNSIGNED_SHORT_1_5_5_5_REV ) +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 // same as GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT +#endif +#if !defined( GL_UNSIGNED_INT_8_8_8_8 ) +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 // same as GL_UNSIGNED_INT_8_8_8_8_EXT +#endif +#if !defined( GL_UNSIGNED_INT_8_8_8_8_REV ) +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 // same as GL_UNSIGNED_INT_8_8_8_8_REV_EXT +#endif +#if !defined( GL_UNSIGNED_INT_10_10_10_2 ) +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 // same as GL_UNSIGNED_INT_10_10_10_2_EXT +#endif +#if !defined( GL_UNSIGNED_INT_2_10_10_10_REV ) +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 // same as GL_UNSIGNED_INT_2_10_10_10_REV_EXT +#endif +#if !defined( GL_UNSIGNED_INT_10F_11F_11F_REV ) +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B // same as GL_UNSIGNED_INT_10F_11F_11F_REV_EXT +#endif +#if !defined( GL_UNSIGNED_INT_5_9_9_9_REV ) +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E // same as GL_UNSIGNED_INT_5_9_9_9_REV_EXT +#endif +#if !defined( GL_UNSIGNED_INT_24_8 ) +#define GL_UNSIGNED_INT_24_8 0x84FA // same as GL_UNSIGNED_INT_24_8_NV and GL_UNSIGNED_INT_24_8_EXT and GL_UNSIGNED_INT_24_8_OES +#endif +#if !defined( GL_FLOAT_32_UNSIGNED_INT_24_8_REV ) +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD // same as GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV and GL_FLOAT_32_UNSIGNED_INT_24_8_REV_ARB +#endif + +/* +================================================================================================================================ + +Internal format to glTexImage2D, glTexImage3D, glCompressedTexImage2D, glCompressedTexImage3D, glTexStorage2D, glTexStorage3D + +================================================================================================================================ +*/ + +// +// 8 bits per component +// + +#if !defined( GL_R8 ) +#define GL_R8 0x8229 // same as GL_R8_EXT +#endif +#if !defined( GL_RG8 ) +#define GL_RG8 0x822B // same as GL_RG8_EXT +#endif +#if !defined( GL_RGB8 ) +#define GL_RGB8 0x8051 // same as GL_RGB8_EXT and GL_RGB8_OES +#endif +#if !defined( GL_RGBA8 ) +#define GL_RGBA8 0x8058 // same as GL_RGBA8_EXT and GL_RGBA8_OES +#endif + +#if !defined( GL_R8_SNORM ) +#define GL_R8_SNORM 0x8F94 +#endif +#if !defined( GL_RG8_SNORM ) +#define GL_RG8_SNORM 0x8F95 +#endif +#if !defined( GL_RGB8_SNORM ) +#define GL_RGB8_SNORM 0x8F96 +#endif +#if !defined( GL_RGBA8_SNORM ) +#define GL_RGBA8_SNORM 0x8F97 +#endif + +#if !defined( GL_R8UI ) +#define GL_R8UI 0x8232 +#endif +#if !defined( GL_RG8UI ) +#define GL_RG8UI 0x8238 +#endif +#if !defined( GL_RGB8UI ) +#define GL_RGB8UI 0x8D7D // same as GL_RGB8UI_EXT +#endif +#if !defined( GL_RGBA8UI ) +#define GL_RGBA8UI 0x8D7C // same as GL_RGBA8UI_EXT +#endif + +#if !defined( GL_R8I ) +#define GL_R8I 0x8231 +#endif +#if !defined( GL_RG8I ) +#define GL_RG8I 0x8237 +#endif +#if !defined( GL_RGB8I ) +#define GL_RGB8I 0x8D8F // same as GL_RGB8I_EXT +#endif +#if !defined( GL_RGBA8I ) +#define GL_RGBA8I 0x8D8E // same as GL_RGBA8I_EXT +#endif + +#if !defined( GL_SR8 ) +#define GL_SR8 0x8FBD // same as GL_SR8_EXT +#endif +#if !defined( GL_SRG8 ) +#define GL_SRG8 0x8FBE // same as GL_SRG8_EXT +#endif +#if !defined( GL_SRGB8 ) +#define GL_SRGB8 0x8C41 // same as GL_SRGB8_EXT +#endif +#if !defined( GL_SRGB8_ALPHA8 ) +#define GL_SRGB8_ALPHA8 0x8C43 // same as GL_SRGB8_ALPHA8_EXT +#endif + +// +// 16 bits per component +// + +#if !defined( GL_R16 ) +#define GL_R16 0x822A // same as GL_R16_EXT +#endif +#if !defined( GL_RG16 ) +#define GL_RG16 0x822C // same as GL_RG16_EXT +#endif +#if !defined( GL_RGB16 ) +#define GL_RGB16 0x8054 // same as GL_RGB16_EXT +#endif +#if !defined( GL_RGBA16 ) +#define GL_RGBA16 0x805B // same as GL_RGBA16_EXT +#endif + +#if !defined( GL_R16_SNORM ) +#define GL_R16_SNORM 0x8F98 // same as GL_R16_SNORM_EXT +#endif +#if !defined( GL_RG16_SNORM ) +#define GL_RG16_SNORM 0x8F99 // same as GL_RG16_SNORM_EXT +#endif +#if !defined( GL_RGB16_SNORM ) +#define GL_RGB16_SNORM 0x8F9A // same as GL_RGB16_SNORM_EXT +#endif +#if !defined( GL_RGBA16_SNORM ) +#define GL_RGBA16_SNORM 0x8F9B // same as GL_RGBA16_SNORM_EXT +#endif + +#if !defined( GL_R16UI ) +#define GL_R16UI 0x8234 +#endif +#if !defined( GL_RG16UI ) +#define GL_RG16UI 0x823A +#endif +#if !defined( GL_RGB16UI ) +#define GL_RGB16UI 0x8D77 // same as GL_RGB16UI_EXT +#endif +#if !defined( GL_RGBA16UI ) +#define GL_RGBA16UI 0x8D76 // same as GL_RGBA16UI_EXT +#endif + +#if !defined( GL_R16I ) +#define GL_R16I 0x8233 +#endif +#if !defined( GL_RG16I ) +#define GL_RG16I 0x8239 +#endif +#if !defined( GL_RGB16I ) +#define GL_RGB16I 0x8D89 // same as GL_RGB16I_EXT +#endif +#if !defined( GL_RGBA16I ) +#define GL_RGBA16I 0x8D88 // same as GL_RGBA16I_EXT +#endif + +#if !defined( GL_R16F ) +#define GL_R16F 0x822D // same as GL_R16F_EXT +#endif +#if !defined( GL_RG16F ) +#define GL_RG16F 0x822F // same as GL_RG16F_EXT +#endif +#if !defined( GL_RGB16F ) +#define GL_RGB16F 0x881B // same as GL_RGB16F_EXT and GL_RGB16F_ARB +#endif +#if !defined( GL_RGBA16F ) +#define GL_RGBA16F 0x881A // sama as GL_RGBA16F_EXT and GL_RGBA16F_ARB +#endif + +// +// 32 bits per component +// + +#if !defined( GL_R32UI ) +#define GL_R32UI 0x8236 +#endif +#if !defined( GL_RG32UI ) +#define GL_RG32UI 0x823C +#endif +#if !defined( GL_RGB32UI ) +#define GL_RGB32UI 0x8D71 // same as GL_RGB32UI_EXT +#endif +#if !defined( GL_RGBA32UI ) +#define GL_RGBA32UI 0x8D70 // same as GL_RGBA32UI_EXT +#endif + +#if !defined( GL_R32I ) +#define GL_R32I 0x8235 +#endif +#if !defined( GL_RG32I ) +#define GL_RG32I 0x823B +#endif +#if !defined( GL_RGB32I ) +#define GL_RGB32I 0x8D83 // same as GL_RGB32I_EXT +#endif +#if !defined( GL_RGBA32I ) +#define GL_RGBA32I 0x8D82 // same as GL_RGBA32I_EXT +#endif + +#if !defined( GL_R32F ) +#define GL_R32F 0x822E // same as GL_R32F_EXT +#endif +#if !defined( GL_RG32F ) +#define GL_RG32F 0x8230 // same as GL_RG32F_EXT +#endif +#if !defined( GL_RGB32F ) +#define GL_RGB32F 0x8815 // same as GL_RGB32F_EXT and GL_RGB32F_ARB +#endif +#if !defined( GL_RGBA32F ) +#define GL_RGBA32F 0x8814 // same as GL_RGBA32F_EXT and GL_RGBA32F_ARB +#endif + +// +// Packed +// + +#if !defined( GL_R3_G3_B2 ) +#define GL_R3_G3_B2 0x2A10 +#endif +#if !defined( GL_RGB4 ) +#define GL_RGB4 0x804F // same as GL_RGB4_EXT +#endif +#if !defined( GL_RGB5 ) +#define GL_RGB5 0x8050 // same as GL_RGB5_EXT +#endif +#if !defined( GL_RGB565 ) +#define GL_RGB565 0x8D62 // same as GL_RGB565_EXT and GL_RGB565_OES +#endif +#if !defined( GL_RGB10 ) +#define GL_RGB10 0x8052 // same as GL_RGB10_EXT +#endif +#if !defined( GL_RGB12 ) +#define GL_RGB12 0x8053 // same as GL_RGB12_EXT +#endif +#if !defined( GL_RGBA2 ) +#define GL_RGBA2 0x8055 // same as GL_RGBA2_EXT +#endif +#if !defined( GL_RGBA4 ) +#define GL_RGBA4 0x8056 // same as GL_RGBA4_EXT and GL_RGBA4_OES +#endif +#if !defined( GL_RGBA12 ) +#define GL_RGBA12 0x805A // same as GL_RGBA12_EXT +#endif +#if !defined( GL_RGB5_A1 ) +#define GL_RGB5_A1 0x8057 // same as GL_RGB5_A1_EXT and GL_RGB5_A1_OES +#endif +#if !defined( GL_RGB10_A2 ) +#define GL_RGB10_A2 0x8059 // same as GL_RGB10_A2_EXT +#endif +#if !defined( GL_RGB10_A2UI ) +#define GL_RGB10_A2UI 0x906F +#endif +#if !defined( GL_R11F_G11F_B10F ) +#define GL_R11F_G11F_B10F 0x8C3A // same as GL_R11F_G11F_B10F_APPLE and GL_R11F_G11F_B10F_EXT +#endif +#if !defined( GL_RGB9_E5 ) +#define GL_RGB9_E5 0x8C3D // same as GL_RGB9_E5_APPLE and GL_RGB9_E5_EXT +#endif + +// +// Alpha +// + +#if !defined( GL_ALPHA4 ) +#define GL_ALPHA4 0x803B // deprecated, same as GL_ALPHA4_EXT +#endif +#if !defined( GL_ALPHA8 ) +#define GL_ALPHA8 0x803C // deprecated, same as GL_ALPHA8_EXT +#endif +#if !defined( GL_ALPHA8_SNORM ) +#define GL_ALPHA8_SNORM 0x9014 // deprecated +#endif +#if !defined( GL_ALPHA8UI_EXT ) +#define GL_ALPHA8UI_EXT 0x8D7E // deprecated +#endif +#if !defined( GL_ALPHA8I_EXT ) +#define GL_ALPHA8I_EXT 0x8D90 // deprecated +#endif +#if !defined( GL_ALPHA12 ) +#define GL_ALPHA12 0x803D // deprecated, same as GL_ALPHA12_EXT +#endif +#if !defined( GL_ALPHA16 ) +#define GL_ALPHA16 0x803E // deprecated, same as GL_ALPHA16_EXT +#endif +#if !defined( GL_ALPHA16_SNORM ) +#define GL_ALPHA16_SNORM 0x9018 // deprecated +#endif +#if !defined( GL_ALPHA16UI_EXT ) +#define GL_ALPHA16UI_EXT 0x8D78 // deprecated +#endif +#if !defined( GL_ALPHA16I_EXT ) +#define GL_ALPHA16I_EXT 0x8D8A // deprecated +#endif +#if !defined( GL_ALPHA16F_ARB ) +#define GL_ALPHA16F_ARB 0x881C // deprecated, same as GL_ALPHA_FLOAT16_APPLE and GL_ALPHA_FLOAT16_ATI +#endif +#if !defined( GL_ALPHA32UI_EXT ) +#define GL_ALPHA32UI_EXT 0x8D72 // deprecated +#endif +#if !defined( GL_ALPHA32I_EXT ) +#define GL_ALPHA32I_EXT 0x8D84 // deprecated +#endif +#if !defined( GL_ALPHA32F_ARB ) +#define GL_ALPHA32F_ARB 0x8816 // deprecated, same as GL_ALPHA_FLOAT32_APPLE and GL_ALPHA_FLOAT32_ATI +#endif + +// +// Luminance +// + +#if !defined( GL_LUMINANCE4 ) +#define GL_LUMINANCE4 0x803F // deprecated, same as GL_LUMINANCE4_EXT +#endif +#if !defined( GL_LUMINANCE8 ) +#define GL_LUMINANCE8 0x8040 // deprecated, same as GL_LUMINANCE8_EXT +#endif +#if !defined( GL_LUMINANCE8_SNORM ) +#define GL_LUMINANCE8_SNORM 0x9015 // deprecated +#endif +#if !defined( GL_SLUMINANCE8 ) +#define GL_SLUMINANCE8 0x8C47 // deprecated, same as GL_SLUMINANCE8_EXT +#endif +#if !defined( GL_LUMINANCE8UI_EXT ) +#define GL_LUMINANCE8UI_EXT 0x8D80 // deprecated +#endif +#if !defined( GL_LUMINANCE8I_EXT ) +#define GL_LUMINANCE8I_EXT 0x8D92 // deprecated +#endif +#if !defined( GL_LUMINANCE12 ) +#define GL_LUMINANCE12 0x8041 // deprecated, same as GL_LUMINANCE12_EXT +#endif +#if !defined( GL_LUMINANCE16 ) +#define GL_LUMINANCE16 0x8042 // deprecated, same as GL_LUMINANCE16_EXT +#endif +#if !defined( GL_LUMINANCE16_SNORM ) +#define GL_LUMINANCE16_SNORM 0x9019 // deprecated +#endif +#if !defined( GL_LUMINANCE16UI_EXT ) +#define GL_LUMINANCE16UI_EXT 0x8D7A // deprecated +#endif +#if !defined( GL_LUMINANCE16I_EXT ) +#define GL_LUMINANCE16I_EXT 0x8D8C // deprecated +#endif +#if !defined( GL_LUMINANCE16F_ARB ) +#define GL_LUMINANCE16F_ARB 0x881E // deprecated, same as GL_LUMINANCE_FLOAT16_APPLE and GL_LUMINANCE_FLOAT16_ATI +#endif +#if !defined( GL_LUMINANCE32UI_EXT ) +#define GL_LUMINANCE32UI_EXT 0x8D74 // deprecated +#endif +#if !defined( GL_LUMINANCE32I_EXT ) +#define GL_LUMINANCE32I_EXT 0x8D86 // deprecated +#endif +#if !defined( GL_LUMINANCE32F_ARB ) +#define GL_LUMINANCE32F_ARB 0x8818 // deprecated, same as GL_LUMINANCE_FLOAT32_APPLE and GL_LUMINANCE_FLOAT32_ATI +#endif + +// +// Luminance/Alpha +// + +#if !defined( GL_LUMINANCE4_ALPHA4 ) +#define GL_LUMINANCE4_ALPHA4 0x8043 // deprecated, same as GL_LUMINANCE4_ALPHA4_EXT +#endif +#if !defined( GL_LUMINANCE6_ALPHA2 ) +#define GL_LUMINANCE6_ALPHA2 0x8044 // deprecated, same as GL_LUMINANCE6_ALPHA2_EXT +#endif +#if !defined( GL_LUMINANCE8_ALPHA8 ) +#define GL_LUMINANCE8_ALPHA8 0x8045 // deprecated, same as GL_LUMINANCE8_ALPHA8_EXT +#endif +#if !defined( GL_LUMINANCE8_ALPHA8_SNORM ) +#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 // deprecated +#endif +#if !defined( GL_SLUMINANCE8_ALPHA8 ) +#define GL_SLUMINANCE8_ALPHA8 0x8C45 // deprecated, same as GL_SLUMINANCE8_ALPHA8_EXT +#endif +#if !defined( GL_LUMINANCE_ALPHA8UI_EXT ) +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 // deprecated +#endif +#if !defined( GL_LUMINANCE_ALPHA8I_EXT ) +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 // deprecated +#endif +#if !defined( GL_LUMINANCE12_ALPHA4 ) +#define GL_LUMINANCE12_ALPHA4 0x8046 // deprecated, same as GL_LUMINANCE12_ALPHA4_EXT +#endif +#if !defined( GL_LUMINANCE12_ALPHA12 ) +#define GL_LUMINANCE12_ALPHA12 0x8047 // deprecated, same as GL_LUMINANCE12_ALPHA12_EXT +#endif +#if !defined( GL_LUMINANCE16_ALPHA16 ) +#define GL_LUMINANCE16_ALPHA16 0x8048 // deprecated, same as GL_LUMINANCE16_ALPHA16_EXT +#endif +#if !defined( GL_LUMINANCE16_ALPHA16_SNORM ) +#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A // deprecated +#endif +#if !defined( GL_LUMINANCE_ALPHA16UI_EXT ) +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B // deprecated +#endif +#if !defined( GL_LUMINANCE_ALPHA16I_EXT ) +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D // deprecated +#endif +#if !defined( GL_LUMINANCE_ALPHA16F_ARB ) +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F // deprecated, same as GL_LUMINANCE_ALPHA_FLOAT16_APPLE and GL_LUMINANCE_ALPHA_FLOAT16_ATI +#endif +#if !defined( GL_LUMINANCE_ALPHA32UI_EXT ) +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 // deprecated +#endif +#if !defined( GL_LUMINANCE_ALPHA32I_EXT ) +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 // deprecated +#endif +#if !defined( GL_LUMINANCE_ALPHA32F_ARB ) +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 // deprecated, same as GL_LUMINANCE_ALPHA_FLOAT32_APPLE and GL_LUMINANCE_ALPHA_FLOAT32_ATI +#endif + +// +// Intensity +// + +#if !defined( GL_INTENSITY4 ) +#define GL_INTENSITY4 0x804A // deprecated, same as GL_INTENSITY4_EXT +#endif +#if !defined( GL_INTENSITY8 ) +#define GL_INTENSITY8 0x804B // deprecated, same as GL_INTENSITY8_EXT +#endif +#if !defined( GL_INTENSITY8_SNORM ) +#define GL_INTENSITY8_SNORM 0x9017 // deprecated +#endif +#if !defined( GL_INTENSITY8UI_EXT ) +#define GL_INTENSITY8UI_EXT 0x8D7F // deprecated +#endif +#if !defined( GL_INTENSITY8I_EXT ) +#define GL_INTENSITY8I_EXT 0x8D91 // deprecated +#endif +#if !defined( GL_INTENSITY12 ) +#define GL_INTENSITY12 0x804C // deprecated, same as GL_INTENSITY12_EXT +#endif +#if !defined( GL_INTENSITY16 ) +#define GL_INTENSITY16 0x804D // deprecated, same as GL_INTENSITY16_EXT +#endif +#if !defined( GL_INTENSITY16_SNORM ) +#define GL_INTENSITY16_SNORM 0x901B // deprecated +#endif +#if !defined( GL_INTENSITY16UI_EXT ) +#define GL_INTENSITY16UI_EXT 0x8D79 // deprecated +#endif +#if !defined( GL_INTENSITY16I_EXT ) +#define GL_INTENSITY16I_EXT 0x8D8B // deprecated +#endif +#if !defined( GL_INTENSITY16F_ARB ) +#define GL_INTENSITY16F_ARB 0x881D // deprecated, same as GL_INTENSITY_FLOAT16_APPLE and GL_INTENSITY_FLOAT16_ATI +#endif +#if !defined( GL_INTENSITY32UI_EXT ) +#define GL_INTENSITY32UI_EXT 0x8D73 // deprecated +#endif +#if !defined( GL_INTENSITY32I_EXT ) +#define GL_INTENSITY32I_EXT 0x8D85 // deprecated +#endif +#if !defined( GL_INTENSITY32F_ARB ) +#define GL_INTENSITY32F_ARB 0x8817 // deprecated, same as GL_INTENSITY_FLOAT32_APPLE and GL_INTENSITY_FLOAT32_ATI +#endif + +// +// Generic compression +// + +#if !defined( GL_COMPRESSED_RED ) +#define GL_COMPRESSED_RED 0x8225 +#endif +#if !defined( GL_COMPRESSED_ALPHA ) +#define GL_COMPRESSED_ALPHA 0x84E9 // deprecated, same as GL_COMPRESSED_ALPHA_ARB +#endif +#if !defined( GL_COMPRESSED_LUMINANCE ) +#define GL_COMPRESSED_LUMINANCE 0x84EA // deprecated, same as GL_COMPRESSED_LUMINANCE_ARB +#endif +#if !defined( GL_COMPRESSED_SLUMINANCE ) +#define GL_COMPRESSED_SLUMINANCE 0x8C4A // deprecated, same as GL_COMPRESSED_SLUMINANCE_EXT +#endif +#if !defined( GL_COMPRESSED_LUMINANCE_ALPHA ) +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB // deprecated, same as GL_COMPRESSED_LUMINANCE_ALPHA_ARB +#endif +#if !defined( GL_COMPRESSED_SLUMINANCE_ALPHA ) +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B // deprecated, same as GL_COMPRESSED_SLUMINANCE_ALPHA_EXT +#endif +#if !defined( GL_COMPRESSED_INTENSITY ) +#define GL_COMPRESSED_INTENSITY 0x84EC // deprecated, same as GL_COMPRESSED_INTENSITY_ARB +#endif +#if !defined( GL_COMPRESSED_RG ) +#define GL_COMPRESSED_RG 0x8226 +#endif +#if !defined( GL_COMPRESSED_RGB ) +#define GL_COMPRESSED_RGB 0x84ED // same as GL_COMPRESSED_RGB_ARB +#endif +#if !defined( GL_COMPRESSED_RGBA ) +#define GL_COMPRESSED_RGBA 0x84EE // same as GL_COMPRESSED_RGBA_ARB +#endif +#if !defined( GL_COMPRESSED_SRGB ) +#define GL_COMPRESSED_SRGB 0x8C48 // same as GL_COMPRESSED_SRGB_EXT +#endif +#if !defined( GL_COMPRESSED_SRGB_ALPHA ) +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 // same as GL_COMPRESSED_SRGB_ALPHA_EXT +#endif + +// +// FXT1 +// + +#if !defined( GL_COMPRESSED_RGB_FXT1_3DFX ) +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 // deprecated +#endif +#if !defined( GL_COMPRESSED_RGBA_FXT1_3DFX ) +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 // deprecated +#endif + +// +// S3TC/DXT/BC +// + +#if !defined( GL_COMPRESSED_RGB_S3TC_DXT1_EXT ) +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#endif +#if !defined( GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ) +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif +#if !defined( GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ) +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#endif +#if !defined( GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ) +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif + +#if !defined( GL_COMPRESSED_SRGB_S3TC_DXT1_EXT ) +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#endif +#if !defined( GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT ) +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#endif +#if !defined( GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT ) +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#endif +#if !defined( GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT ) +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif + +#if !defined( GL_COMPRESSED_LUMINANCE_LATC1_EXT ) +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#endif +#if !defined( GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT ) +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#endif +#if !defined( GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT ) +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#endif +#if !defined( GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT ) +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif + +#if !defined( GL_COMPRESSED_RED_RGTC1 ) +#define GL_COMPRESSED_RED_RGTC1 0x8DBB // same as GL_COMPRESSED_RED_RGTC1_EXT +#endif +#if !defined( GL_COMPRESSED_RG_RGTC2 ) +#define GL_COMPRESSED_RG_RGTC2 0x8DBD // same as GL_COMPRESSED_RG_RGTC2_EXT +#endif +#if !defined( GL_COMPRESSED_SIGNED_RED_RGTC1 ) +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC // same as GL_COMPRESSED_SIGNED_RED_RGTC1_EXT +#endif +#if !defined( GL_COMPRESSED_SIGNED_RG_RGTC2 ) +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE // same as GL_COMPRESSED_SIGNED_RG_RGTC2_EXT +#endif + +#if !defined( GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT ) +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E // same as GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB +#endif +#if !defined( GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT ) +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F // same as GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB +#endif +#if !defined( GL_COMPRESSED_RGBA_BPTC_UNORM ) +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C // same as GL_COMPRESSED_RGBA_BPTC_UNORM_ARB +#endif +#if !defined( GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM ) +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D // same as GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB +#endif + +// +// ETC +// + +#if !defined( GL_ETC1_RGB8_OES ) +#define GL_ETC1_RGB8_OES 0x8D64 +#endif + +#if !defined( GL_COMPRESSED_RGB8_ETC2 ) +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#endif +#if !defined( GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ) +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#endif +#if !defined( GL_COMPRESSED_RGBA8_ETC2_EAC ) +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif + +#if !defined( GL_COMPRESSED_SRGB8_ETC2 ) +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#endif +#if !defined( GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 ) +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#endif +#if !defined( GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC ) +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#endif + +#if !defined( GL_COMPRESSED_R11_EAC ) +#define GL_COMPRESSED_R11_EAC 0x9270 +#endif +#if !defined( GL_COMPRESSED_RG11_EAC ) +#define GL_COMPRESSED_RG11_EAC 0x9272 +#endif +#if !defined( GL_COMPRESSED_SIGNED_R11_EAC ) +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#endif +#if !defined( GL_COMPRESSED_SIGNED_RG11_EAC ) +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#endif + +// +// PVRTC +// + +#if !defined( GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG ) +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#endif +#if !defined( GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG ) +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG 0x9137 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG 0x9138 +#endif +#if !defined( GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT ) +#define GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT 0x8A54 +#define GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT 0x8A55 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT 0x8A56 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT 0x8A57 +#endif +#if !defined( GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG ) +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1 +#endif + +// +// ASTC +// + +#if !defined( GL_COMPRESSED_RGBA_ASTC_4x4_KHR ) +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#endif + +#if !defined( GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR ) +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif + +#if !defined( GL_COMPRESSED_RGBA_ASTC_3x3x3_OES ) +#define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES 0x93C0 +#define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES 0x93C1 +#define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES 0x93C2 +#define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES 0x93C3 +#define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES 0x93C4 +#define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES 0x93C5 +#define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES 0x93C6 +#define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES 0x93C7 +#define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES 0x93C8 +#define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES 0x93C9 +#endif + +#if !defined( GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES ) +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES 0x93E0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES 0x93E1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES 0x93E2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES 0x93E3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES 0x93E4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES 0x93E5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES 0x93E6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES 0x93E7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES 0x93E8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES 0x93E9 +#endif + +// +// ATC +// + +#if !defined( GL_ATC_RGB_AMD ) +#define GL_ATC_RGB_AMD 0x8C92 +#define GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93 +#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE +#endif + +// +// Palletized (combined palette) +// + +#if !defined( GL_PALETTE4_RGB8_OES ) +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif + +// +// Palletized (separate palette) +// + +#if !defined( GL_COLOR_INDEX1_EXT ) +#define GL_COLOR_INDEX1_EXT 0x80E2 // deprecated +#define GL_COLOR_INDEX2_EXT 0x80E3 // deprecated +#define GL_COLOR_INDEX4_EXT 0x80E4 // deprecated +#define GL_COLOR_INDEX8_EXT 0x80E5 // deprecated +#define GL_COLOR_INDEX12_EXT 0x80E6 // deprecated +#define GL_COLOR_INDEX16_EXT 0x80E7 // deprecated +#endif + +// +// Depth/stencil +// + +#if !defined( GL_DEPTH_COMPONENT16 ) +#define GL_DEPTH_COMPONENT16 0x81A5 // same as GL_DEPTH_COMPONENT16_SGIX and GL_DEPTH_COMPONENT16_ARB +#endif +#if !defined( GL_DEPTH_COMPONENT24 ) +#define GL_DEPTH_COMPONENT24 0x81A6 // same as GL_DEPTH_COMPONENT24_SGIX and GL_DEPTH_COMPONENT24_ARB +#endif +#if !defined( GL_DEPTH_COMPONENT32 ) +#define GL_DEPTH_COMPONENT32 0x81A7 // same as GL_DEPTH_COMPONENT32_SGIX and GL_DEPTH_COMPONENT32_ARB and GL_DEPTH_COMPONENT32_OES +#endif +#if !defined( GL_DEPTH_COMPONENT32F ) +#define GL_DEPTH_COMPONENT32F 0x8CAC // same as GL_DEPTH_COMPONENT32F_ARB +#endif +#if !defined( GL_DEPTH_COMPONENT32F_NV ) +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB // note that this is different from GL_DEPTH_COMPONENT32F +#endif +#if !defined( GL_STENCIL_INDEX1 ) +#define GL_STENCIL_INDEX1 0x8D46 // same as GL_STENCIL_INDEX1_EXT +#endif +#if !defined( GL_STENCIL_INDEX4 ) +#define GL_STENCIL_INDEX4 0x8D47 // same as GL_STENCIL_INDEX4_EXT +#endif +#if !defined( GL_STENCIL_INDEX8 ) +#define GL_STENCIL_INDEX8 0x8D48 // same as GL_STENCIL_INDEX8_EXT +#endif +#if !defined( GL_STENCIL_INDEX16 ) +#define GL_STENCIL_INDEX16 0x8D49 // same as GL_STENCIL_INDEX16_EXT +#endif +#if !defined( GL_DEPTH24_STENCIL8 ) +#define GL_DEPTH24_STENCIL8 0x88F0 // same as GL_DEPTH24_STENCIL8_EXT and GL_DEPTH24_STENCIL8_OES +#endif +#if !defined( GL_DEPTH32F_STENCIL8 ) +#define GL_DEPTH32F_STENCIL8 0x8CAD // same as GL_DEPTH32F_STENCIL8_ARB +#endif +#if !defined( GL_DEPTH32F_STENCIL8_NV ) +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC // note that this is different from GL_DEPTH32F_STENCIL8 +#endif + +static inline GLenum glGetFormatFromInternalFormat( const GLenum internalFormat ) +{ + switch ( internalFormat ) + { + // + // 8 bits per component + // + case GL_R8: return GL_RED; // 1-component, 8-bit unsigned normalized + case GL_RG8: return GL_RG; // 2-component, 8-bit unsigned normalized + case GL_RGB8: return GL_RGB; // 3-component, 8-bit unsigned normalized + case GL_RGBA8: return GL_RGBA; // 4-component, 8-bit unsigned normalized + + case GL_R8_SNORM: return GL_RED; // 1-component, 8-bit signed normalized + case GL_RG8_SNORM: return GL_RG; // 2-component, 8-bit signed normalized + case GL_RGB8_SNORM: return GL_RGB; // 3-component, 8-bit signed normalized + case GL_RGBA8_SNORM: return GL_RGBA; // 4-component, 8-bit signed normalized + + case GL_R8UI: return GL_RED; // 1-component, 8-bit unsigned integer + case GL_RG8UI: return GL_RG; // 2-component, 8-bit unsigned integer + case GL_RGB8UI: return GL_RGB; // 3-component, 8-bit unsigned integer + case GL_RGBA8UI: return GL_RGBA; // 4-component, 8-bit unsigned integer + + case GL_R8I: return GL_RED; // 1-component, 8-bit signed integer + case GL_RG8I: return GL_RG; // 2-component, 8-bit signed integer + case GL_RGB8I: return GL_RGB; // 3-component, 8-bit signed integer + case GL_RGBA8I: return GL_RGBA; // 4-component, 8-bit signed integer + + case GL_SR8: return GL_RED; // 1-component, 8-bit sRGB + case GL_SRG8: return GL_RG; // 2-component, 8-bit sRGB + case GL_SRGB8: return GL_RGB; // 3-component, 8-bit sRGB + case GL_SRGB8_ALPHA8: return GL_RGBA; // 4-component, 8-bit sRGB + + // + // 16 bits per component + // + case GL_R16: return GL_RED; // 1-component, 16-bit unsigned normalized + case GL_RG16: return GL_RG; // 2-component, 16-bit unsigned normalized + case GL_RGB16: return GL_RGB; // 3-component, 16-bit unsigned normalized + case GL_RGBA16: return GL_RGBA; // 4-component, 16-bit unsigned normalized + + case GL_R16_SNORM: return GL_RED; // 1-component, 16-bit signed normalized + case GL_RG16_SNORM: return GL_RG; // 2-component, 16-bit signed normalized + case GL_RGB16_SNORM: return GL_RGB; // 3-component, 16-bit signed normalized + case GL_RGBA16_SNORM: return GL_RGBA; // 4-component, 16-bit signed normalized + + case GL_R16UI: return GL_RED; // 1-component, 16-bit unsigned integer + case GL_RG16UI: return GL_RG; // 2-component, 16-bit unsigned integer + case GL_RGB16UI: return GL_RGB; // 3-component, 16-bit unsigned integer + case GL_RGBA16UI: return GL_RGBA; // 4-component, 16-bit unsigned integer + + case GL_R16I: return GL_RED; // 1-component, 16-bit signed integer + case GL_RG16I: return GL_RG; // 2-component, 16-bit signed integer + case GL_RGB16I: return GL_RGB; // 3-component, 16-bit signed integer + case GL_RGBA16I: return GL_RGBA; // 4-component, 16-bit signed integer + + case GL_R16F: return GL_RED; // 1-component, 16-bit floating-point + case GL_RG16F: return GL_RG; // 2-component, 16-bit floating-point + case GL_RGB16F: return GL_RGB; // 3-component, 16-bit floating-point + case GL_RGBA16F: return GL_RGBA; // 4-component, 16-bit floating-point + + // + // 32 bits per component + // + case GL_R32UI: return GL_RED; // 1-component, 32-bit unsigned integer + case GL_RG32UI: return GL_RG; // 2-component, 32-bit unsigned integer + case GL_RGB32UI: return GL_RGB; // 3-component, 32-bit unsigned integer + case GL_RGBA32UI: return GL_RGBA; // 4-component, 32-bit unsigned integer + + case GL_R32I: return GL_RED; // 1-component, 32-bit signed integer + case GL_RG32I: return GL_RG; // 2-component, 32-bit signed integer + case GL_RGB32I: return GL_RGB; // 3-component, 32-bit signed integer + case GL_RGBA32I: return GL_RGBA; // 4-component, 32-bit signed integer + + case GL_R32F: return GL_RED; // 1-component, 32-bit floating-point + case GL_RG32F: return GL_RG; // 2-component, 32-bit floating-point + case GL_RGB32F: return GL_RGB; // 3-component, 32-bit floating-point + case GL_RGBA32F: return GL_RGBA; // 4-component, 32-bit floating-point + + // + // Packed + // + case GL_R3_G3_B2: return GL_RGB; // 3-component 3:3:2, unsigned normalized + case GL_RGB4: return GL_RGB; // 3-component 4:4:4, unsigned normalized + case GL_RGB5: return GL_RGB; // 3-component 5:5:5, unsigned normalized + case GL_RGB565: return GL_RGB; // 3-component 5:6:5, unsigned normalized + case GL_RGB10: return GL_RGB; // 3-component 10:10:10, unsigned normalized + case GL_RGB12: return GL_RGB; // 3-component 12:12:12, unsigned normalized + case GL_RGBA2: return GL_RGBA; // 4-component 2:2:2:2, unsigned normalized + case GL_RGBA4: return GL_RGBA; // 4-component 4:4:4:4, unsigned normalized + case GL_RGBA12: return GL_RGBA; // 4-component 12:12:12:12, unsigned normalized + case GL_RGB5_A1: return GL_RGBA; // 4-component 5:5:5:1, unsigned normalized + case GL_RGB10_A2: return GL_RGBA; // 4-component 10:10:10:2, unsigned normalized + case GL_RGB10_A2UI: return GL_RGBA; // 4-component 10:10:10:2, unsigned integer + case GL_R11F_G11F_B10F: return GL_RGB; // 3-component 11:11:10, floating-point + case GL_RGB9_E5: return GL_RGB; // 3-component/exp 9:9:9/5, floating-point + + // + // S3TC/DXT/BC + // + + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return GL_RGB; // line through 3D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return GL_RGBA; // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return GL_RGBA; // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return GL_RGBA; // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: return GL_RGB; // line through 3D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return GL_RGBA; // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return GL_RGBA; // line through 3D space plus line through 1D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return GL_RGBA; // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB + + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: return GL_RED; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: return GL_RG; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: return GL_RED; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: return GL_RG; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RED_RGTC1: return GL_RED; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG_RGTC2: return GL_RG; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RED_RGTC1: return GL_RED; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG_RGTC2: return GL_RG; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: return GL_RGB; // 3-component, 4x4 blocks, unsigned floating-point + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: return GL_RGB; // 3-component, 4x4 blocks, signed floating-point + case GL_COMPRESSED_RGBA_BPTC_UNORM: return GL_RGBA; // 4-component, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: return GL_RGBA; // 4-component, 4x4 blocks, sRGB + + // + // ETC + // + case GL_ETC1_RGB8_OES: return GL_RGB; // 3-component ETC1, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_RGB8_ETC2: return GL_RGB; // 3-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: return GL_RGBA; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA8_ETC2_EAC: return GL_RGBA; // 4-component ETC2, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ETC2: return GL_RGB; // 3-component ETC2, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return GL_RGBA; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return GL_RGBA; // 4-component ETC2, 4x4 blocks, sRGB + + case GL_COMPRESSED_R11_EAC: return GL_RED; // 1-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG11_EAC: return GL_RG; // 2-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_R11_EAC: return GL_RED; // 1-component ETC, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG11_EAC: return GL_RG; // 2-component ETC, 4x4 blocks, signed normalized + + // + // PVRTC + // + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: return GL_RGB; // 3-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: return GL_RGB; // 3-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: return GL_RGBA; // 4-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: return GL_RGBA; // 4-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: return GL_RGBA; // 4-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: return GL_RGBA; // 4-component PVRTC, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: return GL_RGB; // 3-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: return GL_RGB; // 3-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: return GL_RGBA; // 4-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: return GL_RGBA; // 4-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: return GL_RGBA; // 4-component PVRTC, 8x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: return GL_RGBA; // 4-component PVRTC, 4x4 blocks, sRGB + + // + // ASTC + // + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: return GL_RGBA; // 4-component ASTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: return GL_RGBA; // 4-component ASTC, 5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: return GL_RGBA; // 4-component ASTC, 5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: return GL_RGBA; // 4-component ASTC, 6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: return GL_RGBA; // 4-component ASTC, 6x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: return GL_RGBA; // 4-component ASTC, 8x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: return GL_RGBA; // 4-component ASTC, 8x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: return GL_RGBA; // 4-component ASTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: return GL_RGBA; // 4-component ASTC, 10x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: return GL_RGBA; // 4-component ASTC, 10x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: return GL_RGBA; // 4-component ASTC, 10x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: return GL_RGBA; // 4-component ASTC, 10x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: return GL_RGBA; // 4-component ASTC, 12x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: return GL_RGBA; // 4-component ASTC, 12x12 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: return GL_RGBA; // 4-component ASTC, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: return GL_RGBA; // 4-component ASTC, 5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: return GL_RGBA; // 4-component ASTC, 5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: return GL_RGBA; // 4-component ASTC, 6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: return GL_RGBA; // 4-component ASTC, 6x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: return GL_RGBA; // 4-component ASTC, 8x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: return GL_RGBA; // 4-component ASTC, 8x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: return GL_RGBA; // 4-component ASTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: return GL_RGBA; // 4-component ASTC, 10x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: return GL_RGBA; // 4-component ASTC, 10x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: return GL_RGBA; // 4-component ASTC, 10x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: return GL_RGBA; // 4-component ASTC, 10x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: return GL_RGBA; // 4-component ASTC, 12x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: return GL_RGBA; // 4-component ASTC, 12x12 blocks, sRGB + + case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: return GL_RGBA; // 4-component ASTC, 3x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: return GL_RGBA; // 4-component ASTC, 4x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: return GL_RGBA; // 4-component ASTC, 4x4x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: return GL_RGBA; // 4-component ASTC, 4x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: return GL_RGBA; // 4-component ASTC, 5x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: return GL_RGBA; // 4-component ASTC, 5x5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: return GL_RGBA; // 4-component ASTC, 5x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: return GL_RGBA; // 4-component ASTC, 6x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: return GL_RGBA; // 4-component ASTC, 6x6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: return GL_RGBA; // 4-component ASTC, 6x6x6 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: return GL_RGBA; // 4-component ASTC, 3x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: return GL_RGBA; // 4-component ASTC, 4x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: return GL_RGBA; // 4-component ASTC, 4x4x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: return GL_RGBA; // 4-component ASTC, 4x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: return GL_RGBA; // 4-component ASTC, 5x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: return GL_RGBA; // 4-component ASTC, 5x5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: return GL_RGBA; // 4-component ASTC, 5x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: return GL_RGBA; // 4-component ASTC, 6x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: return GL_RGBA; // 4-component ASTC, 6x6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: return GL_RGBA; // 4-component ASTC, 6x6x6 blocks, sRGB + + // + // ATC + // + case GL_ATC_RGB_AMD: return GL_RGB; // 3-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: return GL_RGBA; // 4-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: return GL_RGBA; // 4-component, 4x4 blocks, unsigned normalized + + // + // Palletized + // + case GL_PALETTE4_RGB8_OES: return GL_RGB; // 3-component 8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA8_OES: return GL_RGBA; // 4-component 8:8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_R5_G6_B5_OES: return GL_RGB; // 3-component 5:6:5, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA4_OES: return GL_RGBA; // 4-component 4:4:4:4, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGB5_A1_OES: return GL_RGBA; // 4-component 5:5:5:1, 4-bit palette, unsigned normalized + case GL_PALETTE8_RGB8_OES: return GL_RGB; // 3-component 8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA8_OES: return GL_RGBA; // 4-component 8:8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_R5_G6_B5_OES: return GL_RGB; // 3-component 5:6:5, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA4_OES: return GL_RGBA; // 4-component 4:4:4:4, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGB5_A1_OES: return GL_RGBA; // 4-component 5:5:5:1, 8-bit palette, unsigned normalized + + // + // Depth/stencil + // + case GL_DEPTH_COMPONENT16: return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT24: return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT32: return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT32F_NV: return GL_DEPTH_COMPONENT; + case GL_STENCIL_INDEX1: return GL_STENCIL_INDEX; + case GL_STENCIL_INDEX4: return GL_STENCIL_INDEX; + case GL_STENCIL_INDEX8: return GL_STENCIL_INDEX; + case GL_STENCIL_INDEX16: return GL_STENCIL_INDEX; + case GL_DEPTH24_STENCIL8: return GL_DEPTH_STENCIL; + case GL_DEPTH32F_STENCIL8: return GL_DEPTH_STENCIL; + case GL_DEPTH32F_STENCIL8_NV: return GL_DEPTH_STENCIL; + + default: return GL_INVALID_VALUE; + } +} + +static inline GLenum glGetTypeFromInternalFormat( const GLenum internalFormat ) +{ + switch ( internalFormat ) + { + // + // 8 bits per component + // + case GL_R8: return GL_UNSIGNED_BYTE; // 1-component, 8-bit unsigned normalized + case GL_RG8: return GL_UNSIGNED_BYTE; // 2-component, 8-bit unsigned normalized + case GL_RGB8: return GL_UNSIGNED_BYTE; // 3-component, 8-bit unsigned normalized + case GL_RGBA8: return GL_UNSIGNED_BYTE; // 4-component, 8-bit unsigned normalized + + case GL_R8_SNORM: return GL_BYTE; // 1-component, 8-bit signed normalized + case GL_RG8_SNORM: return GL_BYTE; // 2-component, 8-bit signed normalized + case GL_RGB8_SNORM: return GL_BYTE; // 3-component, 8-bit signed normalized + case GL_RGBA8_SNORM: return GL_BYTE; // 4-component, 8-bit signed normalized + + case GL_R8UI: return GL_UNSIGNED_BYTE; // 1-component, 8-bit unsigned integer + case GL_RG8UI: return GL_UNSIGNED_BYTE; // 2-component, 8-bit unsigned integer + case GL_RGB8UI: return GL_UNSIGNED_BYTE; // 3-component, 8-bit unsigned integer + case GL_RGBA8UI: return GL_UNSIGNED_BYTE; // 4-component, 8-bit unsigned integer + + case GL_R8I: return GL_BYTE; // 1-component, 8-bit signed integer + case GL_RG8I: return GL_BYTE; // 2-component, 8-bit signed integer + case GL_RGB8I: return GL_BYTE; // 3-component, 8-bit signed integer + case GL_RGBA8I: return GL_BYTE; // 4-component, 8-bit signed integer + + case GL_SR8: return GL_UNSIGNED_BYTE; // 1-component, 8-bit sRGB + case GL_SRG8: return GL_UNSIGNED_BYTE; // 2-component, 8-bit sRGB + case GL_SRGB8: return GL_UNSIGNED_BYTE; // 3-component, 8-bit sRGB + case GL_SRGB8_ALPHA8: return GL_UNSIGNED_BYTE; // 4-component, 8-bit sRGB + + // + // 16 bits per component + // + case GL_R16: return GL_UNSIGNED_SHORT; // 1-component, 16-bit unsigned normalized + case GL_RG16: return GL_UNSIGNED_SHORT; // 2-component, 16-bit unsigned normalized + case GL_RGB16: return GL_UNSIGNED_SHORT; // 3-component, 16-bit unsigned normalized + case GL_RGBA16: return GL_UNSIGNED_SHORT; // 4-component, 16-bit unsigned normalized + + case GL_R16_SNORM: return GL_SHORT; // 1-component, 16-bit signed normalized + case GL_RG16_SNORM: return GL_SHORT; // 2-component, 16-bit signed normalized + case GL_RGB16_SNORM: return GL_SHORT; // 3-component, 16-bit signed normalized + case GL_RGBA16_SNORM: return GL_SHORT; // 4-component, 16-bit signed normalized + + case GL_R16UI: return GL_UNSIGNED_SHORT; // 1-component, 16-bit unsigned integer + case GL_RG16UI: return GL_UNSIGNED_SHORT; // 2-component, 16-bit unsigned integer + case GL_RGB16UI: return GL_UNSIGNED_SHORT; // 3-component, 16-bit unsigned integer + case GL_RGBA16UI: return GL_UNSIGNED_SHORT; // 4-component, 16-bit unsigned integer + + case GL_R16I: return GL_SHORT; // 1-component, 16-bit signed integer + case GL_RG16I: return GL_SHORT; // 2-component, 16-bit signed integer + case GL_RGB16I: return GL_SHORT; // 3-component, 16-bit signed integer + case GL_RGBA16I: return GL_SHORT; // 4-component, 16-bit signed integer + + case GL_R16F: return GL_HALF_FLOAT; // 1-component, 16-bit floating-point + case GL_RG16F: return GL_HALF_FLOAT; // 2-component, 16-bit floating-point + case GL_RGB16F: return GL_HALF_FLOAT; // 3-component, 16-bit floating-point + case GL_RGBA16F: return GL_HALF_FLOAT; // 4-component, 16-bit floating-point + + // + // 32 bits per component + // + case GL_R32UI: return GL_UNSIGNED_INT; // 1-component, 32-bit unsigned integer + case GL_RG32UI: return GL_UNSIGNED_INT; // 2-component, 32-bit unsigned integer + case GL_RGB32UI: return GL_UNSIGNED_INT; // 3-component, 32-bit unsigned integer + case GL_RGBA32UI: return GL_UNSIGNED_INT; // 4-component, 32-bit unsigned integer + + case GL_R32I: return GL_INT; // 1-component, 32-bit signed integer + case GL_RG32I: return GL_INT; // 2-component, 32-bit signed integer + case GL_RGB32I: return GL_INT; // 3-component, 32-bit signed integer + case GL_RGBA32I: return GL_INT; // 4-component, 32-bit signed integer + + case GL_R32F: return GL_FLOAT; // 1-component, 32-bit floating-point + case GL_RG32F: return GL_FLOAT; // 2-component, 32-bit floating-point + case GL_RGB32F: return GL_FLOAT; // 3-component, 32-bit floating-point + case GL_RGBA32F: return GL_FLOAT; // 4-component, 32-bit floating-point + + // + // Packed + // + case GL_R3_G3_B2: return GL_UNSIGNED_BYTE_2_3_3_REV; // 3-component 3:3:2, unsigned normalized + case GL_RGB4: return GL_UNSIGNED_SHORT_4_4_4_4; // 3-component 4:4:4, unsigned normalized + case GL_RGB5: return GL_UNSIGNED_SHORT_5_5_5_1; // 3-component 5:5:5, unsigned normalized + case GL_RGB565: return GL_UNSIGNED_SHORT_5_6_5; // 3-component 5:6:5, unsigned normalized + case GL_RGB10: return GL_UNSIGNED_INT_10_10_10_2; // 3-component 10:10:10, unsigned normalized + case GL_RGB12: return GL_UNSIGNED_SHORT; // 3-component 12:12:12, unsigned normalized + case GL_RGBA2: return GL_UNSIGNED_BYTE; // 4-component 2:2:2:2, unsigned normalized + case GL_RGBA4: return GL_UNSIGNED_SHORT_4_4_4_4; // 4-component 4:4:4:4, unsigned normalized + case GL_RGBA12: return GL_UNSIGNED_SHORT; // 4-component 12:12:12:12, unsigned normalized + case GL_RGB5_A1: return GL_UNSIGNED_SHORT_5_5_5_1; // 4-component 5:5:5:1, unsigned normalized + case GL_RGB10_A2: return GL_UNSIGNED_INT_2_10_10_10_REV; // 4-component 10:10:10:2, unsigned normalized + case GL_RGB10_A2UI: return GL_UNSIGNED_INT_2_10_10_10_REV; // 4-component 10:10:10:2, unsigned integer + case GL_R11F_G11F_B10F: return GL_UNSIGNED_INT_10F_11F_11F_REV; // 3-component 11:11:10, floating-point + case GL_RGB9_E5: return GL_UNSIGNED_INT_5_9_9_9_REV; // 3-component/exp 9:9:9/5, floating-point + + // + // S3TC/DXT/BC + // + + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return GL_UNSIGNED_BYTE; // line through 3D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return GL_UNSIGNED_BYTE; // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return GL_UNSIGNED_BYTE; // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return GL_UNSIGNED_BYTE; // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: return GL_UNSIGNED_BYTE; // line through 3D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return GL_UNSIGNED_BYTE; // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return GL_UNSIGNED_BYTE; // line through 3D space plus line through 1D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return GL_UNSIGNED_BYTE; // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB + + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: return GL_UNSIGNED_BYTE; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: return GL_UNSIGNED_BYTE; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: return GL_UNSIGNED_BYTE; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: return GL_UNSIGNED_BYTE; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RED_RGTC1: return GL_UNSIGNED_BYTE; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG_RGTC2: return GL_UNSIGNED_BYTE; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RED_RGTC1: return GL_UNSIGNED_BYTE; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG_RGTC2: return GL_UNSIGNED_BYTE; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: return GL_FLOAT; // 3-component, 4x4 blocks, unsigned floating-point + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: return GL_FLOAT; // 3-component, 4x4 blocks, signed floating-point + case GL_COMPRESSED_RGBA_BPTC_UNORM: return GL_UNSIGNED_BYTE; // 4-component, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: return GL_UNSIGNED_BYTE; // 4-component, 4x4 blocks, sRGB + + // + // ETC + // + case GL_ETC1_RGB8_OES: return GL_UNSIGNED_BYTE; // 3-component ETC1, 4x4 blocks, unsigned normalized" ), + + case GL_COMPRESSED_RGB8_ETC2: return GL_UNSIGNED_BYTE; // 3-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: return GL_UNSIGNED_BYTE; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA8_ETC2_EAC: return GL_UNSIGNED_BYTE; // 4-component ETC2, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ETC2: return GL_UNSIGNED_BYTE; // 3-component ETC2, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return GL_UNSIGNED_BYTE; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return GL_UNSIGNED_BYTE; // 4-component ETC2, 4x4 blocks, sRGB + + case GL_COMPRESSED_R11_EAC: return GL_UNSIGNED_BYTE; // 1-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG11_EAC: return GL_UNSIGNED_BYTE; // 2-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_R11_EAC: return GL_UNSIGNED_BYTE; // 1-component ETC, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG11_EAC: return GL_UNSIGNED_BYTE; // 2-component ETC, 4x4 blocks, signed normalized + + // + // PVRTC + // + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: return GL_UNSIGNED_BYTE; // 3-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: return GL_UNSIGNED_BYTE; // 3-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: return GL_UNSIGNED_BYTE; // 4-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: return GL_UNSIGNED_BYTE; // 4-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: return GL_UNSIGNED_BYTE; // 4-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: return GL_UNSIGNED_BYTE; // 4-component PVRTC, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: return GL_UNSIGNED_BYTE; // 3-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: return GL_UNSIGNED_BYTE; // 3-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: return GL_UNSIGNED_BYTE; // 4-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: return GL_UNSIGNED_BYTE; // 4-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: return GL_UNSIGNED_BYTE; // 4-component PVRTC, 8x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: return GL_UNSIGNED_BYTE; // 4-component PVRTC, 4x4 blocks, sRGB + + // + // ASTC + // + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 12x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 12x12 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 12x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: return GL_UNSIGNED_BYTE; // 4-component ASTC, 12x12 blocks, sRGB + + case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 3x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6x6 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 3x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6x6 blocks, sRGB + + // + // ATC + // + case GL_ATC_RGB_AMD: return GL_UNSIGNED_BYTE; // 3-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: return GL_UNSIGNED_BYTE; // 4-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: return GL_UNSIGNED_BYTE; // 4-component, 4x4 blocks, unsigned normalized + + // + // Palletized + // + case GL_PALETTE4_RGB8_OES: return GL_UNSIGNED_BYTE; // 3-component 8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA8_OES: return GL_UNSIGNED_BYTE; // 4-component 8:8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_R5_G6_B5_OES: return GL_UNSIGNED_SHORT_5_6_5; // 3-component 5:6:5, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA4_OES: return GL_UNSIGNED_SHORT_4_4_4_4; // 4-component 4:4:4:4, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGB5_A1_OES: return GL_UNSIGNED_SHORT_5_5_5_1; // 4-component 5:5:5:1, 4-bit palette, unsigned normalized + case GL_PALETTE8_RGB8_OES: return GL_UNSIGNED_BYTE; // 3-component 8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA8_OES: return GL_UNSIGNED_BYTE; // 4-component 8:8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_R5_G6_B5_OES: return GL_UNSIGNED_SHORT_5_6_5; // 3-component 5:6:5, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA4_OES: return GL_UNSIGNED_SHORT_4_4_4_4; // 4-component 4:4:4:4, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGB5_A1_OES: return GL_UNSIGNED_SHORT_5_5_5_1; // 4-component 5:5:5:1, 8-bit palette, unsigned normalized + + // + // Depth/stencil + // + case GL_DEPTH_COMPONENT16: return GL_UNSIGNED_SHORT; + case GL_DEPTH_COMPONENT24: return GL_UNSIGNED_INT_24_8; + case GL_DEPTH_COMPONENT32: return GL_UNSIGNED_INT; + case GL_DEPTH_COMPONENT32F: return GL_FLOAT; + case GL_DEPTH_COMPONENT32F_NV: return GL_FLOAT; + case GL_STENCIL_INDEX1: return GL_UNSIGNED_BYTE; + case GL_STENCIL_INDEX4: return GL_UNSIGNED_BYTE; + case GL_STENCIL_INDEX8: return GL_UNSIGNED_BYTE; + case GL_STENCIL_INDEX16: return GL_UNSIGNED_SHORT; + case GL_DEPTH24_STENCIL8: return GL_UNSIGNED_INT_24_8; + case GL_DEPTH32F_STENCIL8: return GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + case GL_DEPTH32F_STENCIL8_NV: return GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + + default: return GL_INVALID_VALUE; + } +} + +static inline unsigned int glGetTypeSizeFromType(GLenum type) +{ + switch (type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + case GL_UNSIGNED_BYTE_3_3_2: + case GL_UNSIGNED_BYTE_2_3_3_REV: + return 1; + + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_UNSIGNED_SHORT_5_6_5_REV: + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + case GL_HALF_FLOAT: + return 2; + + case GL_INT: + case GL_UNSIGNED_INT: + case GL_UNSIGNED_INT_8_8_8_8: + case GL_UNSIGNED_INT_8_8_8_8_REV: + case GL_UNSIGNED_INT_10_10_10_2: + case GL_UNSIGNED_INT_2_10_10_10_REV: + case GL_UNSIGNED_INT_24_8: + case GL_UNSIGNED_INT_10F_11F_11F_REV: + case GL_UNSIGNED_INT_5_9_9_9_REV: + case GL_FLOAT: + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + return 4; + + default: + return GL_INVALID_VALUE; + } +} + +static inline void glGetFormatSize( const GLenum internalFormat, ktxFormatSize * pFormatSize ) +{ + pFormatSize->minBlocksX = pFormatSize->minBlocksY = 1; + switch ( internalFormat ) + { + // + // 8 bits per component + // + case GL_R8: // 1-component, 8-bit unsigned normalized + case GL_R8_SNORM: // 1-component, 8-bit signed normalized + case GL_R8UI: // 1-component, 8-bit unsigned integer + case GL_R8I: // 1-component, 8-bit signed integer + case GL_SR8: // 1-component, 8-bit sRGB + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RG8: // 2-component, 8-bit unsigned normalized + case GL_RG8_SNORM: // 2-component, 8-bit signed normalized + case GL_RG8UI: // 2-component, 8-bit unsigned integer + case GL_RG8I: // 2-component, 8-bit signed integer + case GL_SRG8: // 2-component, 8-bit sRGB + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB8: // 3-component, 8-bit unsigned normalized + case GL_RGB8_SNORM: // 3-component, 8-bit signed normalized + case GL_RGB8UI: // 3-component, 8-bit unsigned integer + case GL_RGB8I: // 3-component, 8-bit signed integer + case GL_SRGB8: // 3-component, 8-bit sRGB + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 3 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA8: // 4-component, 8-bit unsigned normalized + case GL_RGBA8_SNORM: // 4-component, 8-bit signed normalized + case GL_RGBA8UI: // 4-component, 8-bit unsigned integer + case GL_RGBA8I: // 4-component, 8-bit signed integer + case GL_SRGB8_ALPHA8: // 4-component, 8-bit sRGB + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // 16 bits per component + // + case GL_R16: // 1-component, 16-bit unsigned normalized + case GL_R16_SNORM: // 1-component, 16-bit signed normalized + case GL_R16UI: // 1-component, 16-bit unsigned integer + case GL_R16I: // 1-component, 16-bit signed integer + case GL_R16F: // 1-component, 16-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RG16: // 2-component, 16-bit unsigned normalized + case GL_RG16_SNORM: // 2-component, 16-bit signed normalized + case GL_RG16UI: // 2-component, 16-bit unsigned integer + case GL_RG16I: // 2-component, 16-bit signed integer + case GL_RG16F: // 2-component, 16-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB16: // 3-component, 16-bit unsigned normalized + case GL_RGB16_SNORM: // 3-component, 16-bit signed normalized + case GL_RGB16UI: // 3-component, 16-bit unsigned integer + case GL_RGB16I: // 3-component, 16-bit signed integer + case GL_RGB16F: // 3-component, 16-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 6 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA16: // 4-component, 16-bit unsigned normalized + case GL_RGBA16_SNORM: // 4-component, 16-bit signed normalized + case GL_RGBA16UI: // 4-component, 16-bit unsigned integer + case GL_RGBA16I: // 4-component, 16-bit signed integer + case GL_RGBA16F: // 4-component, 16-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // 32 bits per component + // + case GL_R32UI: // 1-component, 32-bit unsigned integer + case GL_R32I: // 1-component, 32-bit signed integer + case GL_R32F: // 1-component, 32-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RG32UI: // 2-component, 32-bit unsigned integer + case GL_RG32I: // 2-component, 32-bit signed integer + case GL_RG32F: // 2-component, 32-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB32UI: // 3-component, 32-bit unsigned integer + case GL_RGB32I: // 3-component, 32-bit signed integer + case GL_RGB32F: // 3-component, 32-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 12 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA32UI: // 4-component, 32-bit unsigned integer + case GL_RGBA32I: // 4-component, 32-bit signed integer + case GL_RGBA32F: // 4-component, 32-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // Packed + // + case GL_R3_G3_B2: // 3-component 3:3:2, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB4: // 3-component 4:4:4, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 12; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB5: // 3-component 5:5:5, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB565: // 3-component 5:6:5, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB10: // 3-component 10:10:10, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB12: // 3-component 12:12:12, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 36; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA2: // 4-component 2:2:2:2, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA4: // 4-component 4:4:4:4, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA12: // 4-component 12:12:12:12, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 48; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB5_A1: // 4-component 5:5:5:1, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB10_A2: // 4-component 10:10:10:2, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB10_A2UI: // 4-component 10:10:10:2, unsigned integer + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_R11F_G11F_B10F: // 3-component 11:11:10, floating-point + case GL_RGB9_E5: // 3-component/exp 9:9:9/5, floating-point + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // S3TC/DXT/BC + // + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: // line through 3D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: // line through 3D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: // line through 3D space plus line through 1D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: // line through 1D space, 4x4 blocks, signed normalized + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: // two lines through 1D space, 4x4 blocks, signed normalized + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_RED_RGTC1: // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RED_RGTC1: // line through 1D space, 4x4 blocks, signed normalized + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RG_RGTC2: // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RG_RGTC2: // two lines through 1D space, 4x4 blocks, signed normalized + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: // 3-component, 4x4 blocks, unsigned floating-point + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: // 3-component, 4x4 blocks, signed floating-point + case GL_COMPRESSED_RGBA_BPTC_UNORM: // 4-component, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: // 4-component, 4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + // + // ETC + // + case GL_ETC1_RGB8_OES: // 3-component ETC1, 4x4 blocks, unsigned normalized" ), + case GL_COMPRESSED_RGB8_ETC2: // 3-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ETC2: // 3-component ETC2, 4x4 blocks, sRGB + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA8_ETC2_EAC: // 4-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: // 4-component ETC2, 4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_R11_EAC: // 1-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_R11_EAC: // 1-component ETC, 4x4 blocks, signed normalized + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RG11_EAC: // 2-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RG11_EAC: // 2-component ETC, 4x4 blocks, signed normalized + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + // + // PVRTC + // + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: // 3-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: // 3-component PVRTC, 8x4 blocks, sRGB + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: // 4-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: // 4-component PVRTC, 8x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + pFormatSize->minBlocksX = 2; + pFormatSize->minBlocksY = 2; + break; + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: // 3-component PVRTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: // 3-component PVRTC, 4x4 blocks, sRGB + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: // 4-component PVRTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: // 4-component PVRTC, 4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + pFormatSize->minBlocksX = 2; + pFormatSize->minBlocksY = 2; + break; + case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: // 4-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: // 4-component PVRTC, 8x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: // 4-component PVRTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: // 4-component PVRTC, 4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + // + // ASTC + // + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: // 4-component ASTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: // 4-component ASTC, 4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: // 4-component ASTC, 5x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: // 4-component ASTC, 5x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: // 4-component ASTC, 5x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: // 4-component ASTC, 5x5 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: // 4-component ASTC, 6x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: // 4-component ASTC, 6x5 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: // 4-component ASTC, 6x6 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: // 4-component ASTC, 6x6 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: // 4-component ASTC, 8x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: // 4-component ASTC, 8x5 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: // 4-component ASTC, 8x6 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: // 4-component ASTC, 8x6 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: // 4-component ASTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: // 4-component ASTC, 8x8 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: // 4-component ASTC, 10x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: // 4-component ASTC, 10x5 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: // 4-component ASTC, 10x6 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: // 4-component ASTC, 10x6 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: // 4-component ASTC, 10x8 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: // 4-component ASTC, 10x8 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: // 4-component ASTC, 10x10 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: // 4-component ASTC, 10x10 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 10; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: // 4-component ASTC, 12x10 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: // 4-component ASTC, 12x10 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 12; + pFormatSize->blockHeight = 10; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: // 4-component ASTC, 12x12 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: // 4-component ASTC, 12x12 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 12; + pFormatSize->blockHeight = 12; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: // 4-component ASTC, 3x3x3 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: // 4-component ASTC, 3x3x3 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 3; + pFormatSize->blockHeight = 3; + pFormatSize->blockDepth = 3; + break; + case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: // 4-component ASTC, 4x3x3 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: // 4-component ASTC, 4x3x3 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 3; + pFormatSize->blockDepth = 3; + break; + case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: // 4-component ASTC, 4x4x3 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: // 4-component ASTC, 4x4x3 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 3; + break; + case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: // 4-component ASTC, 4x4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: // 4-component ASTC, 4x4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 4; + break; + case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: // 4-component ASTC, 5x4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: // 4-component ASTC, 5x4x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 4; + break; + case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: // 4-component ASTC, 5x5x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: // 4-component ASTC, 5x5x4 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 4; + break; + case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: // 4-component ASTC, 5x5x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: // 4-component ASTC, 5x5x5 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 5; + break; + case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: // 4-component ASTC, 6x5x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: // 4-component ASTC, 6x5x5 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 5; + break; + case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: // 4-component ASTC, 6x6x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: // 4-component ASTC, 6x6x5 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 5; + break; + case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: // 4-component ASTC, 6x6x6 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: // 4-component ASTC, 6x6x6 blocks, sRGB + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 6; + break; + + // + // ATC + // + case GL_ATC_RGB_AMD: // 3-component, 4x4 blocks, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: // 4-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: // 4-component, 4x4 blocks, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + // + // Palletized + // + case GL_PALETTE4_RGB8_OES: // 3-component 8:8:8, 4-bit palette, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 16 * 24; + pFormatSize->blockSizeInBits = 4; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE4_RGBA8_OES: // 4-component 8:8:8:8, 4-bit palette, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 16 * 32; + pFormatSize->blockSizeInBits = 4; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE4_R5_G6_B5_OES: // 3-component 5:6:5, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA4_OES: // 4-component 4:4:4:4, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGB5_A1_OES: // 4-component 5:5:5:1, 4-bit palette, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 16 * 16; + pFormatSize->blockSizeInBits = 4; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE8_RGB8_OES: // 3-component 8:8:8, 8-bit palette, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 256 * 24; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE8_RGBA8_OES: // 4-component 8:8:8:8, 8-bit palette, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 256 * 32; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE8_R5_G6_B5_OES: // 3-component 5:6:5, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA4_OES: // 4-component 4:4:4:4, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGB5_A1_OES: // 4-component 5:5:5:1, 8-bit palette, unsigned normalized + pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 256 * 16; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // Depth/stencil + // + case GL_DEPTH_COMPONENT16: + pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + case GL_DEPTH_COMPONENT32F: + case GL_DEPTH_COMPONENT32F_NV: + pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_STENCIL_INDEX1: + pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_STENCIL_INDEX4: + pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_STENCIL_INDEX8: + pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_STENCIL_INDEX16: + pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_DEPTH24_STENCIL8: + pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_DEPTH32F_STENCIL8: + case GL_DEPTH32F_STENCIL8_NV: + pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + default: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 0 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + } +} + +static inline GLint glGetInternalFormatFromVkFormat( VkFormat vkFormat ) +{ + switch ( vkFormat ) + { + // + // 8 bits per component + // + case VK_FORMAT_R8_UNORM: return GL_R8; // 1-component, 8-bit unsigned normalized + case VK_FORMAT_R8G8_UNORM: return GL_RG8; // 2-component, 8-bit unsigned normalized + case VK_FORMAT_R8G8B8_UNORM: return GL_RGB8; // 3-component, 8-bit unsigned normalized + case VK_FORMAT_R8G8B8A8_UNORM: return GL_RGBA8; // 4-component, 8-bit unsigned normalized + + case VK_FORMAT_R8_SNORM: return GL_R8_SNORM; // 1-component, 8-bit signed normalized + case VK_FORMAT_R8G8_SNORM: return GL_RG8_SNORM; // 2-component, 8-bit signed normalized + case VK_FORMAT_R8G8B8_SNORM: return GL_RGB8_SNORM; // 3-component, 8-bit signed normalized + case VK_FORMAT_R8G8B8A8_SNORM: return GL_RGBA8_SNORM; // 4-component, 8-bit signed normalized + + case VK_FORMAT_R8_UINT: return GL_R8UI; // 1-component, 8-bit unsigned integer + case VK_FORMAT_R8G8_UINT: return GL_RG8UI; // 2-component, 8-bit unsigned integer + case VK_FORMAT_R8G8B8_UINT: return GL_RGB8UI; // 3-component, 8-bit unsigned integer + case VK_FORMAT_R8G8B8A8_UINT: return GL_RGBA8UI; // 4-component, 8-bit unsigned integer + + case VK_FORMAT_R8_SINT: return GL_R8I; // 1-component, 8-bit signed integer + case VK_FORMAT_R8G8_SINT: return GL_RG8I; // 2-component, 8-bit signed integer + case VK_FORMAT_R8G8B8_SINT: return GL_RGB8I; // 3-component, 8-bit signed integer + case VK_FORMAT_R8G8B8A8_SINT: return GL_RGBA8I; // 4-component, 8-bit signed integer + + case VK_FORMAT_R8_SRGB: return GL_SR8; // 1-component, 8-bit sRGB + case VK_FORMAT_R8G8_SRGB: return GL_SRG8; // 2-component, 8-bit sRGB + case VK_FORMAT_R8G8B8_SRGB: return GL_SRGB8; // 3-component, 8-bit sRGB + case VK_FORMAT_R8G8B8A8_SRGB: return GL_SRGB8_ALPHA8; // 4-component, 8-bit sRGB + + // + // 16 bits per component + // + case VK_FORMAT_R16_UNORM: return GL_R16; // 1-component, 16-bit unsigned normalized + case VK_FORMAT_R16G16_UNORM: return GL_RG16; // 2-component, 16-bit unsigned normalized + case VK_FORMAT_R16G16B16_UNORM: return GL_RGB16; // 3-component, 16-bit unsigned normalized + case VK_FORMAT_R16G16B16A16_UNORM: return GL_RGBA16; // 4-component, 16-bit unsigned normalized + + case VK_FORMAT_R16_SNORM: return GL_R16_SNORM; // 1-component, 16-bit signed normalized + case VK_FORMAT_R16G16_SNORM: return GL_RG16_SNORM; // 2-component, 16-bit signed normalized + case VK_FORMAT_R16G16B16_SNORM: return GL_RGB16_SNORM; // 3-component, 16-bit signed normalized + case VK_FORMAT_R16G16B16A16_SNORM: return GL_RGBA16_SNORM; // 4-component, 16-bit signed normalized + + case VK_FORMAT_R16_UINT: return GL_R16UI; // 1-component, 16-bit unsigned integer + case VK_FORMAT_R16G16_UINT: return GL_RG16UI; // 2-component, 16-bit unsigned integer + case VK_FORMAT_R16G16B16_UINT: return GL_RGB16UI; // 3-component, 16-bit unsigned integer + case VK_FORMAT_R16G16B16A16_UINT: return GL_RGBA16UI; // 4-component, 16-bit unsigned integer + + case VK_FORMAT_R16_SINT: return GL_R16I; // 1-component, 16-bit signed integer + case VK_FORMAT_R16G16_SINT: return GL_RG16I; // 2-component, 16-bit signed integer + case VK_FORMAT_R16G16B16_SINT: return GL_RGB16I; // 3-component, 16-bit signed integer + case VK_FORMAT_R16G16B16A16_SINT: return GL_RGBA16I; // 4-component, 16-bit signed integer + + case VK_FORMAT_R16_SFLOAT: return GL_R16F; // 1-component, 16-bit floating-point + case VK_FORMAT_R16G16_SFLOAT: return GL_RG16F; // 2-component, 16-bit floating-point + case VK_FORMAT_R16G16B16_SFLOAT: return GL_RGB16F; // 3-component, 16-bit floating-point + case VK_FORMAT_R16G16B16A16_SFLOAT: return GL_RGBA16F; // 4-component, 16-bit floating-point + + // + // 32 bits per component + // + case VK_FORMAT_R32_UINT: return GL_R32UI; // 1-component, 32-bit unsigned integer + case VK_FORMAT_R32G32_UINT: return GL_RG32UI; // 2-component, 32-bit unsigned integer + case VK_FORMAT_R32G32B32_UINT: return GL_RGB32UI; // 3-component, 32-bit unsigned integer + case VK_FORMAT_R32G32B32A32_UINT: return GL_RGBA32UI; // 4-component, 32-bit unsigned integer + + case VK_FORMAT_R32_SINT: return GL_R32I; // 1-component, 32-bit signed integer + case VK_FORMAT_R32G32_SINT: return GL_RG32I; // 2-component, 32-bit signed integer + case VK_FORMAT_R32G32B32_SINT: return GL_RGB32I; // 3-component, 32-bit signed integer + case VK_FORMAT_R32G32B32A32_SINT: return GL_RGBA32I; // 4-component, 32-bit signed integer + + case VK_FORMAT_R32_SFLOAT: return GL_R32F; // 1-component, 32-bit floating-point + case VK_FORMAT_R32G32_SFLOAT: return GL_RG32F; // 2-component, 32-bit floating-point + case VK_FORMAT_R32G32B32_SFLOAT: return GL_RGB32F; // 3-component, 32-bit floating-point + case VK_FORMAT_R32G32B32A32_SFLOAT: return GL_RGBA32F; // 4-component, 32-bit floating-point + + // + // Packed + // + case VK_FORMAT_R5G5B5A1_UNORM_PACK16: return GL_RGB5; // 3-component 5:5:5, unsigned normalized + case VK_FORMAT_R5G6B5_UNORM_PACK16: return GL_RGB565; // 3-component 5:6:5, unsigned normalized + case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return GL_RGBA4; // 4-component 4:4:4:4, unsigned normalized + case VK_FORMAT_A1R5G5B5_UNORM_PACK16: return GL_RGB5_A1; // 4-component 5:5:5:1, unsigned normalized + case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return GL_RGB10_A2; // 4-component 10:10:10:2, unsigned normalized + case VK_FORMAT_A2R10G10B10_UINT_PACK32: return GL_RGB10_A2UI; // 4-component 10:10:10:2, unsigned integer + case VK_FORMAT_B10G11R11_UFLOAT_PACK32: return GL_R11F_G11F_B10F; // 3-component 11:11:10, floating-point + case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: return GL_RGB9_E5; // 3-component/exp 9:9:9/5, floating-point + + // + // S3TC/DXT/BC + // + + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; // line through 3D space, 4x4 blocks, unsigned normalized + case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized + case VK_FORMAT_BC2_UNORM_BLOCK: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized + case VK_FORMAT_BC3_UNORM_BLOCK: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized + + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT; // line through 3D space, 4x4 blocks, sRGB + case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB + case VK_FORMAT_BC2_SRGB_BLOCK: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; // line through 3D space plus line through 1D space, 4x4 blocks, sRGB + case VK_FORMAT_BC3_SRGB_BLOCK: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB + + case VK_FORMAT_BC4_UNORM_BLOCK: return GL_COMPRESSED_RED_RGTC1; // line through 1D space, 4x4 blocks, unsigned normalized + case VK_FORMAT_BC5_UNORM_BLOCK: return GL_COMPRESSED_RG_RGTC2; // two lines through 1D space, 4x4 blocks, unsigned normalized + case VK_FORMAT_BC4_SNORM_BLOCK: return GL_COMPRESSED_SIGNED_RED_RGTC1; // line through 1D space, 4x4 blocks, signed normalized + case VK_FORMAT_BC5_SNORM_BLOCK: return GL_COMPRESSED_SIGNED_RG_RGTC2; // two lines through 1D space, 4x4 blocks, signed normalized + + case VK_FORMAT_BC6H_UFLOAT_BLOCK: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; // 3-component, 4x4 blocks, unsigned floating-point + case VK_FORMAT_BC6H_SFLOAT_BLOCK: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; // 3-component, 4x4 blocks, signed floating-point + case VK_FORMAT_BC7_UNORM_BLOCK: return GL_COMPRESSED_RGBA_BPTC_UNORM; // 4-component, 4x4 blocks, unsigned normalized + case VK_FORMAT_BC7_SRGB_BLOCK: return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; // 4-component, 4x4 blocks, sRGB + + // + // ETC + // + case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: return GL_COMPRESSED_RGB8_ETC2; // 3-component ETC2, 4x4 blocks, unsigned normalized + case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized + case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: return GL_COMPRESSED_RGBA8_ETC2_EAC; // 4-component ETC2, 4x4 blocks, unsigned normalized + + case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ETC2; // 3-component ETC2, 4x4 blocks, sRGB + case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB + case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; // 4-component ETC2, 4x4 blocks, sRGB + + case VK_FORMAT_EAC_R11_UNORM_BLOCK: return GL_COMPRESSED_R11_EAC; // 1-component ETC, 4x4 blocks, unsigned normalized + case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: return GL_COMPRESSED_RG11_EAC; // 2-component ETC, 4x4 blocks, unsigned normalized + case VK_FORMAT_EAC_R11_SNORM_BLOCK: return GL_COMPRESSED_SIGNED_R11_EAC; // 1-component ETC, 4x4 blocks, signed normalized + case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: return GL_COMPRESSED_SIGNED_RG11_EAC; // 2-component ETC, 4x4 blocks, signed normalized + + // + // PVRTC + // + case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; // 3- or 4-component PVRTC, 16x8 blocks, unsigned normalized + case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; // 3- or 4-component PVRTC, 8x8 blocks, unsigned normalized + case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: return GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG; // 3- or 4-component PVRTC, 16x8 blocks, unsigned normalized + case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: return GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG; // 3- or 4-component PVRTC, 4x4 blocks, unsigned normalized + + case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT; // 4-component PVRTC, 16x8 blocks, sRGB + case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT; // 4-component PVRTC, 8x8 blocks, sRGB + case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG; // 4-component PVRTC, 8x4 blocks, sRGB + case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG; // 4-component PVRTC, 4x4 blocks, sRGB + + // + // ASTC + // + case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; // 4-component ASTC, 4x4 blocks, unsigned normalized + case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_5x4_KHR; // 4-component ASTC, 5x4 blocks, unsigned normalized + case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_5x5_KHR; // 4-component ASTC, 5x5 blocks, unsigned normalized + case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_6x5_KHR; // 4-component ASTC, 6x5 blocks, unsigned normalized + case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_6x6_KHR; // 4-component ASTC, 6x6 blocks, unsigned normalized + case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_8x5_KHR; // 4-component ASTC, 8x5 blocks, unsigned normalized + case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_8x6_KHR; // 4-component ASTC, 8x6 blocks, unsigned normalized + case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_8x8_KHR; // 4-component ASTC, 8x8 blocks, unsigned normalized + case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x5_KHR; // 4-component ASTC, 10x5 blocks, unsigned normalized + case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x6_KHR; // 4-component ASTC, 10x6 blocks, unsigned normalized + case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x8_KHR; // 4-component ASTC, 10x8 blocks, unsigned normalized + case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x10_KHR; // 4-component ASTC, 10x10 blocks, unsigned normalized + case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_12x10_KHR; // 4-component ASTC, 12x10 blocks, unsigned normalized + case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_12x12_KHR; // 4-component ASTC, 12x12 blocks, unsigned normalized + + case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; // 4-component ASTC, 4x4 blocks, sRGB + case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR; // 4-component ASTC, 5x4 blocks, sRGB + case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR; // 4-component ASTC, 5x5 blocks, sRGB + case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR; // 4-component ASTC, 6x5 blocks, sRGB + case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR; // 4-component ASTC, 6x6 blocks, sRGB + case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR; // 4-component ASTC, 8x5 blocks, sRGB + case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR; // 4-component ASTC, 8x6 blocks, sRGB + case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR; // 4-component ASTC, 8x8 blocks, sRGB + case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR; // 4-component ASTC, 10x5 blocks, sRGB + case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR; // 4-component ASTC, 10x6 blocks, sRGB + case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR; // 4-component ASTC, 10x8 blocks, sRGB + case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR; // 4-component ASTC, 10x10 blocks, sRGB + case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR; // 4-component ASTC, 12x10 blocks, sRGB + case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR; // 4-component ASTC, 12x12 blocks, sRGB + + // XXX FIXME Update once Vulkan ASTC HDR & 3D extensions are released. +#if 0 + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_3x3x3_OES; // 4-component ASTC, 3x3x3 blocks, unsigned normalized + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_4x3x3_OES; // 4-component ASTC, 4x3x3 blocks, unsigned normalized + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_4x4x3_OES; // 4-component ASTC, 4x4x3 blocks, unsigned normalized + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_4x4x4_OES; // 4-component ASTC, 4x4x4 blocks, unsigned normalized + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_5x4x4_OES; // 4-component ASTC, 5x4x4 blocks, unsigned normalized + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_5x5x4_OES; // 4-component ASTC, 5x5x4 blocks, unsigned normalized + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_5x5x5_OES; // 4-component ASTC, 5x5x5 blocks, unsigned normalized + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_6x5x5_OES; // 4-component ASTC, 6x5x5 blocks, unsigned normalized + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_6x6x5_OES; // 4-component ASTC, 6x6x5 blocks, unsigned normalized + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_RGBA_ASTC_6x6x6_OES; // 4-component ASTC, 6x6x6 blocks, unsigned normalized + + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES; // 4-component ASTC, 3x3x3 blocks, sRGB + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES; // 4-component ASTC, 4x3x3 blocks, sRGB + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES; // 4-component ASTC, 4x4x3 blocks, sRGB + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES; // 4-component ASTC, 4x4x4 blocks, sRGB + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES; // 4-component ASTC, 5x4x4 blocks, sRGB + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES; // 4-component ASTC, 5x5x4 blocks, sRGB + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES; // 4-component ASTC, 5x5x5 blocks, sRGB + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES; // 4-component ASTC, 6x5x5 blocks, sRGB + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES; // 4-component ASTC, 6x6x5 blocks, sRGB + case VK_FORMAT_UNDEFINED: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES; // 4-component ASTC, 6x6x6 blocks, sRGB +#endif + + // + // Depth/stencil + // + case VK_FORMAT_D16_UNORM: return GL_DEPTH_COMPONENT16; + case VK_FORMAT_X8_D24_UNORM_PACK32: return GL_DEPTH_COMPONENT24; + case VK_FORMAT_D32_SFLOAT: return GL_DEPTH_COMPONENT32F; + case VK_FORMAT_S8_UINT: return GL_STENCIL_INDEX8; + case VK_FORMAT_D24_UNORM_S8_UINT: return GL_DEPTH24_STENCIL8; + case VK_FORMAT_D32_SFLOAT_S8_UINT: return GL_DEPTH32F_STENCIL8; + + default: return GL_INVALID_VALUE; + } +} + +#endif // !GL_FORMAT_H diff --git a/thirdparty/libktx/lib/hashlist.c b/thirdparty/libktx/lib/hashlist.c new file mode 100644 index 0000000000..0ca89fc561 --- /dev/null +++ b/thirdparty/libktx/lib/hashlist.c @@ -0,0 +1,604 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2010-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file hashlist.c + * @~English + * + * @brief Functions for creating and using a hash list of key-value + * pairs. + * + * @author Mark Callow, HI Corporation + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +// This is to avoid compile warnings. strlen is defined as returning +// size_t and is used by the uthash macros. This avoids having to +// make changes to uthash and a bunch of casts in this file. The +// casts would be required because the key and value lengths in KTX +// are specified as 4 byte quantities so we can't change _keyAndValue +// below to use size_t. +#define strlen(x) ((unsigned int)strlen(x)) + +#include "uthash.h" + +#include "ktx.h" +#include "ktxint.h" + + +/** + * @internal + * @struct ktxKVListEntry + * @brief Hash list entry structure + */ +typedef struct ktxKVListEntry { + unsigned int keyLen; /*!< Length of the key */ + char* key; /*!< Pointer to key string */ + unsigned int valueLen; /*!< Length of the value */ + void* value; /*!< Pointer to the value */ + UT_hash_handle hh; /*!< handle used by UT hash */ +} ktxKVListEntry; + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Construct an empty hash list for storing key-value pairs. + * + * @param [in] pHead pointer to the location to write the list head. + */ +void +ktxHashList_Construct(ktxHashList* pHead) +{ + *pHead = NULL; +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Construct a hash list by copying another. + * + * @param [in] pHead pointer to head of the list. + * @param [in] orig head of the original hash list. + */ +void +ktxHashList_ConstructCopy(ktxHashList* pHead, ktxHashList orig) +{ + ktxHashListEntry* entry = orig; + *pHead = NULL; + for (; entry != NULL; entry = ktxHashList_Next(entry)) { + (void)ktxHashList_AddKVPair(pHead, + entry->key, entry->valueLen, entry->value); + } +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Destruct a hash list. + * + * All memory associated with the hash list's keys and values + * is freed. + * + * @param [in] pHead pointer to the hash list to be destroyed. + */ +void +ktxHashList_Destruct(ktxHashList* pHead) +{ + ktxKVListEntry* kv; + ktxKVListEntry* head = *pHead; + + for(kv = head; kv != NULL;) { + ktxKVListEntry* tmp = (ktxKVListEntry*)kv->hh.next; + HASH_DELETE(hh, head, kv); + free(kv); + kv = tmp; + } +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Create an empty hash list for storing key-value pairs. + * + * @param [in,out] ppHl address of a variable in which to set a pointer to + * the newly created hash list. + * + * @return KTX_SUCCESS or one of the following error codes. + * @exception KTX_OUT_OF_MEMORY if not enough memory. + */ +KTX_error_code +ktxHashList_Create(ktxHashList** ppHl) +{ + ktxHashList* hl = (ktxHashList*)malloc(sizeof (ktxKVListEntry*)); + if (hl == NULL) + return KTX_OUT_OF_MEMORY; + + ktxHashList_Construct(hl); + *ppHl = hl; + return KTX_SUCCESS; +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Create a copy of a hash list. + * + * @param [in,out] ppHl address of a variable in which to set a pointer to + * the newly created hash list. + * @param [in] orig head of the ktxHashList to copy. + * + * @return KTX_SUCCESS or one of the following error codes. + * @exception KTX_OUT_OF_MEMORY if not enough memory. + */ +KTX_error_code +ktxHashList_CreateCopy(ktxHashList** ppHl, ktxHashList orig) +{ + ktxHashList* hl = (ktxHashList*)malloc(sizeof (ktxKVListEntry*)); + if (hl == NULL) + return KTX_OUT_OF_MEMORY; + + ktxHashList_ConstructCopy(hl, orig); + *ppHl = hl; + return KTX_SUCCESS; +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Destroy a hash list. + * + * All memory associated with the hash list's keys and values + * is freed. The hash list is also freed. + * + * @param [in] pHead pointer to the hash list to be destroyed. + */ +void +ktxHashList_Destroy(ktxHashList* pHead) +{ + ktxHashList_Destruct(pHead); + free(pHead); +} + +#if !__clang__ && __GNUC__ // Grumble clang grumble +// These are in uthash.h macros. I don't want to change that file. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif + +/** + * @memberof ktxHashList @public + * @~English + * @brief Add a key value pair to a hash list. + * + * The value can be empty, i.e, its length can be 0. + * + * @param [in] pHead pointer to the head of the target hash list. + * @param [in] key pointer to the UTF8 NUL-terminated string to be used as the key. + * @param [in] valueLen the number of bytes of data in @p value. + * @param [in] value pointer to the bytes of data constituting the value. + * + * @return KTX_SUCCESS or one of the following error codes. + * @exception KTX_INVALID_VALUE if @p pHead, @p key or @p value are NULL, @p key is an + * empty string or @p valueLen == 0. + */ +KTX_error_code +ktxHashList_AddKVPair(ktxHashList* pHead, const char* key, unsigned int valueLen, const void* value) +{ + if (pHead && key && (valueLen == 0 || value)) { + unsigned int keyLen = (unsigned int)strlen(key) + 1; + ktxKVListEntry* kv; + + if (keyLen == 1) + return KTX_INVALID_VALUE; /* Empty string */ + + /* Allocate all the memory as a block */ + kv = (ktxKVListEntry*)malloc(sizeof(ktxKVListEntry) + keyLen + valueLen); + /* Put key first */ + kv->key = (char *)kv + sizeof(ktxKVListEntry); + kv->keyLen = keyLen; + memcpy(kv->key, key, keyLen); + /* then value */ + kv->valueLen = valueLen; + if (valueLen > 0) { + kv->value = kv->key + keyLen; + memcpy(kv->value, value, valueLen); + } else { + kv->value = 0; + } + + HASH_ADD_KEYPTR( hh, *pHead, kv->key, kv->keyLen-1, kv); + return KTX_SUCCESS; + } else + return KTX_INVALID_VALUE; +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Delete a key value pair in a hash list. + * + * Is a nop if the key is not in the hash. + * + * @param [in] pHead pointer to the head of the target hash list. + * @param [in] key pointer to the UTF8 NUL-terminated string to be used as the key. + * + * @return KTX_SUCCESS or one of the following error codes. + * @exception KTX_INVALID_VALUE if @p pHead is NULL or @p key is an empty + * string. + */ +KTX_error_code +ktxHashList_DeleteKVPair(ktxHashList* pHead, const char* key) +{ + if (pHead && key) { + ktxKVListEntry* kv; + + HASH_FIND_STR( *pHead, key, kv ); /* kv: pointer to target entry. */ + if (kv != NULL) + HASH_DEL(*pHead, kv); + return KTX_SUCCESS; + } else + return KTX_INVALID_VALUE; +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Delete an entry from a hash list. + * + * @param [in] pHead pointer to the head of the target hash list. + * @param [in] pEntry pointer to the ktxHashListEntry to delete. + * + * @return KTX_SUCCESS or one of the following error codes. + * @exception KTX_INVALID_VALUE if @p pHead is NULL or @p key is an empty + * string. + */ +KTX_error_code +ktxHashList_DeleteEntry(ktxHashList* pHead, ktxHashListEntry* pEntry) +{ + if (pHead && pEntry) { + HASH_DEL(*pHead, pEntry); + return KTX_SUCCESS; + } else + return KTX_INVALID_VALUE; +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Looks up a key in a hash list and returns the entry. + * + * @param [in] pHead pointer to the head of the target hash list. + * @param [in] key pointer to a UTF8 NUL-terminated string to find. + * @param [in,out] ppEntry @p *ppEntry is set to the point at the + * ktxHashListEntry. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p This, @p key or @p pValueLen or @p ppValue + * is NULL. + * @exception KTX_NOT_FOUND an entry matching @p key was not found. + */ +KTX_error_code +ktxHashList_FindEntry(ktxHashList* pHead, const char* key, + ktxHashListEntry** ppEntry) +{ + if (pHead && key) { + ktxKVListEntry* kv; + + HASH_FIND_STR( *pHead, key, kv ); /* kv: output pointer */ + + if (kv) { + *ppEntry = kv; + return KTX_SUCCESS; + } else + return KTX_NOT_FOUND; + } else + return KTX_INVALID_VALUE; +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Looks up a key in a hash list and returns the value. + * + * @param [in] pHead pointer to the head of the target hash list. + * @param [in] key pointer to a UTF8 NUL-terminated string to find. + * @param [in,out] pValueLen @p *pValueLen is set to the number of bytes of + * data in the returned value. + * @param [in,out] ppValue @p *ppValue is set to the point to the value for + * @p key. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p This, @p key or @p pValueLen or @p ppValue + * is NULL. + * @exception KTX_NOT_FOUND an entry matching @p key was not found. + */ +KTX_error_code +ktxHashList_FindValue(ktxHashList *pHead, const char* key, unsigned int* pValueLen, void** ppValue) +{ + if (pValueLen && ppValue) { + ktxHashListEntry* pEntry; + KTX_error_code result; + + result = ktxHashList_FindEntry(pHead, key, &pEntry); + if (result == KTX_SUCCESS) { + ktxHashListEntry_GetValue(pEntry, pValueLen, ppValue); + return KTX_SUCCESS; + } else + return result; + } else + return KTX_INVALID_VALUE; +} + +#if !__clang__ && __GNUC__ +#pragma GCC diagnostic pop +#endif + +/** + * @memberof ktxHashList @public + * @~English + * @brief Returns the next entry in a ktxHashList. + * + * Use for iterating through the list: + * @code + * ktxHashListEntry* entry; + * for (entry = listHead; entry != NULL; entry = ktxHashList_Next(entry)) { + * ... + * }; + * @endcode + * + * Note + * + * @param [in] entry pointer to a hash list entry. Note that a ktxHashList*, + * i.e. the list head, is also a pointer to an entry so + * can be passed to this function. + * + * @return a pointer to the next entry or NULL. + * + */ +ktxHashListEntry* +ktxHashList_Next(ktxHashListEntry* entry) +{ + if (entry) { + return ((ktxKVListEntry*)entry)->hh.next; + } else + return NULL; +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Serialize a hash list to a block of data suitable for writing + * to a file. + * + * The caller is responsible for freeing the data block returned by this + * function. + * + * @param [in] pHead pointer to the head of the target hash list. + * @param [in,out] pKvdLen @p *pKvdLen is set to the number of bytes of + * data in the returned data block. + * @param [in,out] ppKvd @p *ppKvd is set to the point to the block of + * memory containing the serialized data or + * NULL. if the hash list is empty. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p This, @p pKvdLen or @p ppKvd is NULL. + * @exception KTX_OUT_OF_MEMORY there was not enough memory to serialize the + * data. + */ +KTX_error_code +ktxHashList_Serialize(ktxHashList* pHead, + unsigned int* pKvdLen, unsigned char** ppKvd) +{ + + if (pHead && pKvdLen && ppKvd) { + ktxKVListEntry* kv; + unsigned int bytesOfKeyValueData = 0; + unsigned int keyValueLen; + unsigned char* sd; + char padding[4] = {0, 0, 0, 0}; + + for (kv = *pHead; kv != NULL; kv = kv->hh.next) { + /* sizeof(sd) is to make space to write keyAndValueByteSize */ + keyValueLen = kv->keyLen + kv->valueLen + sizeof(ktx_uint32_t); + /* Add valuePadding */ + keyValueLen = _KTX_PAD4(keyValueLen); + bytesOfKeyValueData += keyValueLen; + } + + if (bytesOfKeyValueData == 0) { + *pKvdLen = 0; + *ppKvd = NULL; + } else { + sd = malloc(bytesOfKeyValueData); + if (!sd) + return KTX_OUT_OF_MEMORY; + + *pKvdLen = bytesOfKeyValueData; + *ppKvd = sd; + + for (kv = *pHead; kv != NULL; kv = kv->hh.next) { + int padLen; + + keyValueLen = kv->keyLen + kv->valueLen; + *(ktx_uint32_t*)sd = keyValueLen; + sd += sizeof(ktx_uint32_t); + memcpy(sd, kv->key, kv->keyLen); + sd += kv->keyLen; + if (kv->valueLen > 0) + memcpy(sd, kv->value, kv->valueLen); + sd += kv->valueLen; + padLen = _KTX_PAD4_LEN(keyValueLen); + memcpy(sd, padding, padLen); + sd += padLen; + } + } + return KTX_SUCCESS; + } else + return KTX_INVALID_VALUE; +} + + +int sort_by_key_codepoint(ktxKVListEntry* a, ktxKVListEntry* b) { + return strcmp(a->key, b->key); +} + +/** + * @memberof ktxHashList @public + * @~English + * @brief Sort a hash list in order of the UTF8 codepoints. + * + * @param [in] pHead pointer to the head of the target hash list. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p This is NULL. + */ +KTX_error_code +ktxHashList_Sort(ktxHashList* pHead) +{ + if (pHead) { + //ktxKVListEntry* kv = (ktxKVListEntry*)pHead; + + HASH_SORT(*pHead, sort_by_key_codepoint); + return KTX_SUCCESS; + } else { + return KTX_INVALID_VALUE; + } +} + + +/** + * @memberof ktxHashList @public + * @~English + * @brief Construct a hash list from a block of serialized key-value + * data read from a file. + * @note The bytes of the 32-bit key-value lengths within the serialized data + * are expected to be in native endianness. + * + * @param [in] pHead pointer to the head of the target hash list. + * @param [in] kvdLen the length of the serialized key-value data. + * @param [in] pKvd pointer to the serialized key-value data. + * table. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_OPERATION if @p pHead does not point to an empty list. + * @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0. + * @exception KTX_OUT_OF_MEMORY there was not enough memory to create the hash + * table. + */ +KTX_error_code +ktxHashList_Deserialize(ktxHashList* pHead, unsigned int kvdLen, void* pKvd) +{ + char* src = pKvd; + KTX_error_code result; + + if (kvdLen == 0 || pKvd == NULL || pHead == NULL) + return KTX_INVALID_VALUE; + + if (*pHead != NULL) + return KTX_INVALID_OPERATION; + + result = KTX_SUCCESS; + while (result == KTX_SUCCESS && src < (char *)pKvd + kvdLen) { + char* key; + unsigned int keyLen, valueLen; + void* value; + ktx_uint32_t keyAndValueByteSize = *((ktx_uint32_t*)src); + + src += sizeof(keyAndValueByteSize); + key = src; + keyLen = (unsigned int)strlen(key) + 1; + value = key + keyLen; + + valueLen = keyAndValueByteSize - keyLen; + result = ktxHashList_AddKVPair(pHead, key, valueLen, + valueLen > 0 ? value : NULL); + if (result == KTX_SUCCESS) { + src += _KTX_PAD4(keyAndValueByteSize); + } + } + return result; +} + + +/** + * @memberof ktxHashListEntry @public + * @~English + * @brief Return the key of a ktxHashListEntry + * + * @param [in] This The target hash list entry. + * @param [in,out] pKeyLen @p *pKeyLen is set to the byte length of + * the returned key. + * @param [in,out] ppKey @p *ppKey is set to the point to the value of + * @p the key. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0. + */ +KTX_error_code +ktxHashListEntry_GetKey(ktxHashListEntry* This, + unsigned int* pKeyLen, char** ppKey) +{ + if (pKeyLen && ppKey) { + ktxKVListEntry* kv = (ktxKVListEntry*)This; + *pKeyLen = kv->keyLen; + *ppKey = kv->key; + return KTX_SUCCESS; + } else + return KTX_INVALID_VALUE; +} + + +/** + * @memberof ktxHashListEntry @public + * @~English + * @brief Return the value from a ktxHashListEntry + * + * @param [in] This The target hash list entry. + * @param [in,out] pValueLen @p *pValueLen is set to the number of bytes of + * data in the returned value. + * @param [in,out] ppValue @p *ppValue is set to point to the value of + * of the target entry. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0. + */ +KTX_error_code +ktxHashListEntry_GetValue(ktxHashListEntry* This, + unsigned int* pValueLen, void** ppValue) +{ + if (pValueLen && ppValue) { + ktxKVListEntry* kv = (ktxKVListEntry*)This; + *pValueLen = kv->valueLen; + *ppValue = kv->valueLen > 0 ? kv->value : NULL; + return KTX_SUCCESS; + } else + return KTX_INVALID_VALUE; +} diff --git a/thirdparty/libktx/lib/ktxint.h b/thirdparty/libktx/lib/ktxint.h new file mode 100644 index 0000000000..03c9945ce7 --- /dev/null +++ b/thirdparty/libktx/lib/ktxint.h @@ -0,0 +1,266 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* $Id: e36ad79b5eac8ea237d6a05602c71aadab575519 $ */ + +/* + * Copyright 2010-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + + +/* + * Author: Mark Callow from original code by Georg Kolling + */ + +#ifndef KTXINT_H +#define KTXINT_H + +#include <math.h> + +/* Define this to include the ETC unpack software in the library. */ +#ifndef SUPPORT_SOFTWARE_ETC_UNPACK + /* Include for all GL versions because have seen OpenGL ES 3 + * implementaions that do not support ETC1 (ARM Mali emulator v1.0)! + */ + #define SUPPORT_SOFTWARE_ETC_UNPACK 1 +#endif + +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#define QUOTE(x) #x +#define STR(x) QUOTE(x) + +#define KTX2_IDENTIFIER_REF { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A } +#define KTX2_HEADER_SIZE (80) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @internal + * @brief used to pass GL context capabilites to subroutines. + */ +#define _KTX_NO_R16_FORMATS 0x0 +#define _KTX_R16_FORMATS_NORM 0x1 +#define _KTX_R16_FORMATS_SNORM 0x2 +#define _KTX_ALL_R16_FORMATS (_KTX_R16_FORMATS_NORM | _KTX_R16_FORMATS_SNORM) +extern GLint _ktxR16Formats; +extern GLboolean _ktxSupportsSRGB; + +/** + * @internal + * @~English + * @brief KTX file header. + * + * See the KTX specification for descriptions. + */ +typedef struct KTX_header { + ktx_uint8_t identifier[12]; + ktx_uint32_t endianness; + ktx_uint32_t glType; + ktx_uint32_t glTypeSize; + ktx_uint32_t glFormat; + ktx_uint32_t glInternalformat; + ktx_uint32_t glBaseInternalformat; + ktx_uint32_t pixelWidth; + ktx_uint32_t pixelHeight; + ktx_uint32_t pixelDepth; + ktx_uint32_t numberOfArrayElements; + ktx_uint32_t numberOfFaces; + ktx_uint32_t numberOfMipLevels; + ktx_uint32_t bytesOfKeyValueData; +} KTX_header; + +/* This will cause compilation to fail if the struct size doesn't match */ +typedef int KTX_header_SIZE_ASSERT [sizeof(KTX_header) == KTX_HEADER_SIZE]; + +/** + * @internal + * @~English + * @brief 32-bit KTX 2 index entry. + */ +typedef struct ktxIndexEntry32 { + ktx_uint32_t byteOffset; /*!< Offset of item from start of file. */ + ktx_uint32_t byteLength; /*!< Number of bytes of data in the item. */ +} ktxIndexEntry32; +/** + * @internal + * @~English + * @brief 64-bit KTX 2 index entry. + */ +typedef struct ktxIndexEntry64 { + ktx_uint64_t byteOffset; /*!< Offset of item from start of file. */ + ktx_uint64_t byteLength; /*!< Number of bytes of data in the item. */ +} ktxIndexEntry64; + +/** + * @internal + * @~English + * @brief KTX 2 file header. + * + * See the KTX 2 specification for descriptions. + */ +typedef struct KTX_header2 { + ktx_uint8_t identifier[12]; + ktx_uint32_t vkFormat; + ktx_uint32_t typeSize; + ktx_uint32_t pixelWidth; + ktx_uint32_t pixelHeight; + ktx_uint32_t pixelDepth; + ktx_uint32_t layerCount; + ktx_uint32_t faceCount; + ktx_uint32_t levelCount; + ktx_uint32_t supercompressionScheme; + ktxIndexEntry32 dataFormatDescriptor; + ktxIndexEntry32 keyValueData; + ktxIndexEntry64 supercompressionGlobalData; +} KTX_header2; + +/* This will cause compilation to fail if the struct size doesn't match */ +typedef int KTX_header2_SIZE_ASSERT [sizeof(KTX_header2) == KTX2_HEADER_SIZE]; + +/** + * @internal + * @~English + * @brief KTX 2 level index entry. + */ +typedef struct ktxLevelIndexEntry { + ktx_uint64_t byteOffset; /*!< Offset of level from start of file. */ + ktx_uint64_t byteLength; + /*!< Number of bytes of compressed image data in the level. */ + ktx_uint64_t uncompressedByteLength; + /*!< Number of bytes of uncompressed image data in the level. */ +} ktxLevelIndexEntry; + +/** + * @internal + * @~English + * @brief Structure for supplemental information about the texture. + * + * _ktxCheckHeader returns supplemental information about the texture in this + * structure that is derived during checking of the file header. + */ +typedef struct KTX_supplemental_info +{ + ktx_uint8_t compressed; + ktx_uint8_t generateMipmaps; + ktx_uint16_t textureDimension; +} KTX_supplemental_info; +/** + * @internal + * @var ktx_uint8_t KTX_supplemental_info::compressed + * @~English + * @brief KTX_TRUE, if this a compressed texture, KTX_FALSE otherwise? + */ +/** + * @internal + * @var ktx_uint8_t KTX_supplemental_info::generateMipmaps + * @~English + * @brief KTX_TRUE, if mipmap generation is required, KTX_FALSE otherwise. + */ +/** + * @internal + * @var ktx_uint16_t KTX_supplemental_info::textureDimension + * @~English + * @brief The number of dimensions, 1, 2 or 3, of data in the texture image. + */ + +/* + * @internal + * CheckHeader1 + * + * Reads the KTX file header and performs some sanity checking on the values + */ +KTX_error_code ktxCheckHeader1_(KTX_header* pHeader, + KTX_supplemental_info* pSuppInfo); + +/* + * @internal + * CheckHeader2 + * + * Reads the KTX 2 file header and performs some sanity checking on the values + */ +KTX_error_code ktxCheckHeader2_(KTX_header2* pHeader, + KTX_supplemental_info* pSuppInfo); + +/* + * SwapEndian16: Swaps endianness in an array of 16-bit values + */ +void _ktxSwapEndian16(ktx_uint16_t* pData16, ktx_size_t count); + +/* + * SwapEndian32: Swaps endianness in an array of 32-bit values + */ +void _ktxSwapEndian32(ktx_uint32_t* pData32, ktx_size_t count); + +/* + * SwapEndian32: Swaps endianness in an array of 64-bit values + */ +void _ktxSwapEndian64(ktx_uint64_t* pData64, ktx_size_t count); + +/* + * UnpackETC: uncompresses an ETC compressed texture image + */ +KTX_error_code _ktxUnpackETC(const GLubyte* srcETC, const GLenum srcFormat, + ktx_uint32_t active_width, ktx_uint32_t active_height, + GLubyte** dstImage, + GLenum* format, GLenum* internalFormat, GLenum* type, + GLint R16Formats, GLboolean supportsSRGB); + +/* + * Pad nbytes to next multiple of n + */ +#define _KTX_PADN(n, nbytes) (ktx_uint32_t)(n * ceilf((float)(nbytes) / n)) +/* + * Calculate bytes of of padding needed to reach next multiple of n. + */ +/* Equivalent to (n * ceil(nbytes / n)) - nbytes */ +#define _KTX_PADN_LEN(n, nbytes) \ + (ktx_uint32_t)((n * ceilf((float)(nbytes) / n)) - (nbytes)) + +/* + * Pad nbytes to next multiple of 4 + */ +#define _KTX_PAD4(nbytes) _KTX_PADN(4, nbytes) +/* + * Calculate bytes of of padding needed to reach next multiple of 4. + */ +#define _KTX_PAD4_LEN(nbytes) _KTX_PADN_LEN(4, nbytes) + +/* + * Pad nbytes to next multiple of 8 + */ +#define _KTX_PAD8(nbytes) _KTX_PADN(8, nbytes) +/* + * Calculate bytes of of padding needed to reach next multiple of 8. + */ +#define _KTX_PAD8_LEN(nbytes) _KTX_PADN_LEN(8, nbytes) + +/* + * Pad nbytes to KTX_GL_UNPACK_ALIGNMENT + */ +#define _KTX_PAD_UNPACK_ALIGN(nbytes) \ + _KTX_PADN(KTX_GL_UNPACK_ALIGNMENT, nbytes) +/* + * Calculate bytes of of padding needed to reach KTX_GL_UNPACK_ALIGNMENT. + */ +#define _KTX_PAD_UNPACK_ALIGN_LEN(nbytes) \ + _KTX_PADN_LEN(KTX_GL_UNPACK_ALIGNMENT, nbytes) + +/* + ====================================== + Internal utility functions + ====================================== +*/ + +void printKTX2Info2(ktxStream* src, KTX_header2* header); + +#ifdef __cplusplus +} +#endif + +#endif /* KTXINT_H */ diff --git a/thirdparty/libktx/lib/memstream.c b/thirdparty/libktx/lib/memstream.c new file mode 100644 index 0000000000..b963fa70ca --- /dev/null +++ b/thirdparty/libktx/lib/memstream.c @@ -0,0 +1,561 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2010-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * + * @brief Implementation of ktxStream for memory. + * + * @author Maksim Kolesin, Under Development + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +#include "ktx.h" +#include "ktxint.h" +#include "memstream.h" + +/** +* @brief Default allocation size for a ktxMemStream. +*/ +#define KTX_MEM_DEFAULT_ALLOCATED_SIZE 256 + +/** + * @brief Structure to store information about data allocated for ktxMemStream. + */ +struct ktxMem +{ + const ktx_uint8_t* robytes;/*!< pointer to read-only data */ + ktx_uint8_t* bytes; /*!< pointer to rw data. */ + ktx_size_t alloc_size; /*!< allocated size of the memory block. */ + ktx_size_t used_size; /*!< bytes used. Effectively the write position. */ + ktx_off_t pos; /*!< read/write position. */ +}; + +static KTX_error_code ktxMem_expand(ktxMem* pMem, const ktx_size_t size); + +/** + * @brief Initialize a ktxMem struct for read-write. + * + * Memory for the stream data is allocated internally but the + * caller is responsible for freeing the memory. A pointer to + * the memory can be obtained with ktxMem_getdata(). + * + * @sa ktxMem_getdata. + * + * @param [in] pMem pointer to the @c ktxMem to initialize. + */ +static KTX_error_code +ktxMem_construct(ktxMem* pMem) +{ + pMem->pos = 0; + pMem->alloc_size = 0; + pMem->robytes = 0; + pMem->bytes = 0; + pMem->used_size = 0; + return ktxMem_expand(pMem, KTX_MEM_DEFAULT_ALLOCATED_SIZE); +} + +/** + * @brief Create & initialize a ktxMem struct for read-write. + * + * @sa ktxMem_construct. + * + * @param [in,out] ppMem pointer to the location in which to return + * a pointer to the newly created @c ktxMem. + * + * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error. + * + * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory. + */ +static KTX_error_code +ktxMem_create(ktxMem** ppMem) +{ + ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem)); + if (pNewMem) { + KTX_error_code result = ktxMem_construct(pNewMem); + if (result == KTX_SUCCESS) + *ppMem = pNewMem; + return result; + } + else { + return KTX_OUT_OF_MEMORY; + } +} + +/** + * @brief Initialize a ktxMem struct for read-only. + * + * @param [in] pMem pointer to the @c ktxMem to initialize. + * @param [in] bytes pointer to the data to be read. + * @param [in] numBytes number of bytes of data. + */ +static void +ktxMem_construct_ro(ktxMem* pMem, const void* bytes, ktx_size_t numBytes) +{ + pMem->pos = 0; + pMem->robytes = bytes; + pMem->bytes = 0; + pMem->used_size = numBytes; + pMem->alloc_size = numBytes; +} + +/** + * @brief Create & initialize a ktxMem struct for read-only. + * + * @sa ktxMem_construct. + * + * @param [in,out] ppMem pointer to the location in which to return + * a pointer to the newly created @c ktxMem. + * @param [in] bytes pointer to the data to be read. + * @param [in] numBytes number of bytes of data. + * + * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error. + * + * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory. + */ +static KTX_error_code +ktxMem_create_ro(ktxMem** ppMem, const void* bytes, ktx_size_t numBytes) +{ + ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem)); + if (pNewMem) { + ktxMem_construct_ro(pNewMem, bytes, numBytes); + *ppMem = pNewMem; + return KTX_SUCCESS; + } + else { + return KTX_OUT_OF_MEMORY; + } +} + +/* + * ktxMem_destruct not needed as ktxMem_construct caller is reponsible + * for freeing the data written. + */ + +/** + * @brief Free the memory of a struct ktxMem. + * + * @param pMem pointer to ktxMem to free. + */ +static void +ktxMem_destroy(ktxMem* pMem, ktx_bool_t freeData) +{ + assert(pMem != NULL); + if (freeData) { + free(pMem->bytes); + } + free(pMem); +} + +#ifdef KTXMEM_CLEAR_USED +/** + * @brief Clear the data of a memory stream. + * + * @param pMem pointer to ktxMem to clear. + */ +static void +ktxMem_clear(ktxMem* pMem) +{ + assert(pMem != NULL); + memset(pMem, 0, sizeof(ktxMem)); +} +#endif + +/** + * @~English + * @brief Expand a ktxMem to fit to a new size. + * + * @param [in] pMem pointer to ktxMem struct to expand. + * @param [in] newsize minimum new size required. + * + * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error. + * + * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory. + */ +static KTX_error_code +ktxMem_expand(ktxMem *pMem, const ktx_size_t newsize) +{ + ktx_size_t new_alloc_size; + + assert(pMem != NULL && newsize != 0); + + new_alloc_size = pMem->alloc_size == 0 ? + KTX_MEM_DEFAULT_ALLOCATED_SIZE : pMem->alloc_size; + while (new_alloc_size < newsize) { + ktx_size_t alloc_size = new_alloc_size; + new_alloc_size <<= 1; + if (new_alloc_size < alloc_size) { + /* Overflow. Set to maximum size. newsize can't be larger. */ + new_alloc_size = (ktx_size_t)-1L; + } + } + + if (new_alloc_size == pMem->alloc_size) + return KTX_SUCCESS; + + if (!pMem->bytes) + pMem->bytes = (ktx_uint8_t*)malloc(new_alloc_size); + else + pMem->bytes = (ktx_uint8_t*)realloc(pMem->bytes, new_alloc_size); + + if (!pMem->bytes) + { + pMem->alloc_size = 0; + pMem->used_size = 0; + return KTX_OUT_OF_MEMORY; + } + + pMem->alloc_size = new_alloc_size; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Read bytes from a ktxMemStream. + * + * @param [in] str pointer to ktxMem struct, converted to a void*, that + * specifies an input stream. + * @param [in,out] dst pointer to memory where to copy read bytes. + * @param [in,out] count pointer to number of bytes to read. + * + * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error. + * + * @exception KTX_INVALID_VALUE @p str or @p dst is @c NULL or @p str->data is + * @c NULL. + * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request. + */ +static +KTX_error_code ktxMemStream_read(ktxStream* str, void* dst, const ktx_size_t count) +{ + ktxMem* mem; + ktx_off_t newpos; + const ktx_uint8_t* bytes; + + + if (!str || (mem = str->data.mem)== 0) + return KTX_INVALID_VALUE; + + newpos = mem->pos + count; + /* The first clause checks for overflow. */ + if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size) + return KTX_FILE_UNEXPECTED_EOF; + + bytes = mem->robytes ? mem->robytes : mem->bytes; + memcpy(dst, bytes + mem->pos, count); + mem->pos = newpos; + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Skip bytes in a ktxMemStream. + * + * @param [in] str pointer to the ktxStream on which to operate. + * @param [in] count number of bytes to skip. + * + * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error. + * + * @exception KTX_INVALID_VALUE @p str or @p mem is @c NULL or sufficient + * data is not available in ktxMem. + * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request. + */ +static +KTX_error_code ktxMemStream_skip(ktxStream* str, const ktx_size_t count) +{ + ktxMem* mem; + ktx_off_t newpos; + + if (!str || (mem = str->data.mem) == 0) + return KTX_INVALID_VALUE; + + newpos = mem->pos + count; + /* The first clause checks for overflow. */ + if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size) + return KTX_FILE_UNEXPECTED_EOF; + + mem->pos = newpos; + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Write bytes to a ktxMemStream. + * + * @param [out] str pointer to the ktxStream that specifies the destination. + * @param [in] src pointer to the array of elements to be written, + * converted to a const void*. + * @param [in] size size in bytes of each element to be written. + * @param [in] count number of elements, each one with a @p size of size + * bytes. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_OVERFLOW write would result in file exceeding the + * maximum permissible size. + * @exception KTX_INVALID_OPERATION @p str is a read-only stream. + * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p mem is @c NULL. + * @exception KTX_OUT_OF_MEMORY See ktxMem_expand() for causes. + */ +static +KTX_error_code ktxMemStream_write(ktxStream* str, const void* src, + const ktx_size_t size, const ktx_size_t count) +{ + ktxMem* mem; + KTX_error_code rc = KTX_SUCCESS; + ktx_size_t new_size; + + if (!str || (mem = str->data.mem) == 0) + return KTX_INVALID_VALUE; + + if (mem->robytes) + return KTX_INVALID_OPERATION; /* read-only */ + + new_size = mem->pos + (size*count); + //if (new_size < mem->used_size) + if ((ktx_off_t)new_size < mem->pos) + return KTX_FILE_OVERFLOW; + + if (mem->alloc_size < new_size) { + rc = ktxMem_expand(mem, new_size); + if (rc != KTX_SUCCESS) + return rc; + } + + memcpy(mem->bytes + mem->pos, src, size*count); + mem->pos += size*count; + if (mem->pos > (ktx_off_t)mem->used_size) + mem->used_size = mem->pos; + + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Get the current read/write position in a ktxMemStream. + * + * @param [in] str pointer to the ktxStream to query. + * @param [in,out] off pointer to variable to receive the offset value. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str or @p pos is @c NULL. + */ +static +KTX_error_code ktxMemStream_getpos(ktxStream* str, ktx_off_t* const pos) +{ + if (!str || !pos) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeMemory); + + *pos = str->data.mem->pos; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Set the current read/write position in a ktxMemStream. + * + * Offset of 0 is the start of the file. + * + * @param [in] str pointer to the ktxStream whose r/w position is to be set. + * @param [in] off pointer to the offset value to set. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str is @c NULL. + * @exception KTX_INVALID_OPERATION @p pos > size of the allocated memory. + */ +static +KTX_error_code ktxMemStream_setpos(ktxStream* str, ktx_off_t pos) +{ + if (!str) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeMemory); + + if (pos > (ktx_off_t)str->data.mem->alloc_size) + return KTX_INVALID_OPERATION; + + str->data.mem->pos = pos; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Get a pointer to a ktxMemStream's data. + * + * Gets a pointer to data that has been written to the stream. Returned + * pointer will be 0 if stream is read-only. + * + * @param [in] str pointer to the ktxStream whose data pointer is to + * be queried. + * @param [in,out] ppBytes pointer to a variable in which the data pointer + * will be written. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str or @p ppBytes is @c NULL. + */ +KTX_error_code ktxMemStream_getdata(ktxStream* str, ktx_uint8_t** ppBytes) +{ + if (!str || !ppBytes) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeMemory); + + *ppBytes = str->data.mem->bytes; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Get the size of a ktxMemStream in bytes. + * + * @param [in] str pointer to the ktxStream whose size is to be queried. + * @param [in,out] size pointer to a variable in which size will be written. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str or @p pSize is @c NULL. + */ +static +KTX_error_code ktxMemStream_getsize(ktxStream* str, ktx_size_t* pSize) +{ + if (!str || !pSize) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeMemory); + + *pSize = str->data.mem->used_size; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Setup ktxMemStream function pointers. + */ +void +ktxMemStream_setup(ktxStream* str) +{ + str->type = eStreamTypeMemory; + str->read = ktxMemStream_read; + str->skip = ktxMemStream_skip; + str->write = ktxMemStream_write; + str->getpos = ktxMemStream_getpos; + str->setpos = ktxMemStream_setpos; + str->getsize = ktxMemStream_getsize; + str->destruct = ktxMemStream_destruct; +} + +/** + * @~English + * @brief Initialize a read-write ktxMemStream. + * + * Memory is allocated as data is written. The caller of this is + * responsible for freeing this memory unless @a freeOnDestruct + * is not KTX_FALSE. + * + * @param [in] str pointer to a ktxStream struct to initialize. + * @param [in] freeOnDestruct If not KTX_FALSE memory holding the data will + * be freed by the destructor. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str is @c NULL. + * @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory. + */ +KTX_error_code ktxMemStream_construct(ktxStream* str, + ktx_bool_t freeOnDestruct) +{ + ktxMem* mem; + KTX_error_code result = KTX_SUCCESS; + + if (!str) + return KTX_INVALID_VALUE; + + result = ktxMem_create(&mem); + + if (KTX_SUCCESS == result) { + str->data.mem = mem; + ktxMemStream_setup(str); + str->closeOnDestruct = freeOnDestruct; + } + + return result; +} + +/** + * @~English + * @brief Initialize a read-only ktxMemStream. + * + * @param [in] str pointer to a ktxStream struct to initialize. + * @param [in] bytes pointer to an array of bytes containing the data. + * @param [in] numBytes size of array of data for ktxMemStream. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str or @p mem is @c NULL or @p numBytes + * is 0. + * or @p size is less than 0. + * @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory. + */ +KTX_error_code ktxMemStream_construct_ro(ktxStream* str, + const ktx_uint8_t* bytes, + const ktx_size_t numBytes) +{ + ktxMem* mem; + KTX_error_code result = KTX_SUCCESS; + + if (!str || !bytes || numBytes == 0) + return KTX_INVALID_VALUE; + + result = ktxMem_create_ro(&mem, bytes, numBytes); + + if (KTX_SUCCESS == result) { + str->data.mem = mem; + ktxMemStream_setup(str); + str->closeOnDestruct = KTX_FALSE; + } + + return result; +} + +/** + * @~English + * @brief Free the memory used by a ktxMemStream. + * + * This only frees the memory used to store the data written to the stream, + * if the @c freeOnDestruct parameter to ktxMemStream_construct() was not + * @c KTX_FALSE. Otherwise it is the responsibility of the caller of + * ktxMemStream_construct() and a pointer to this memory should be retrieved + * using ktxMemStream_getdata() before calling this function. + * + * @sa ktxMemStream_construct, ktxMemStream_getdata. + * + * @param [in] str pointer to the ktxStream whose memory is + * to be freed. + */ +void +ktxMemStream_destruct(ktxStream* str) +{ + assert(str && str->type == eStreamTypeMemory); + + ktxMem_destroy(str->data.mem, str->closeOnDestruct); + str->data.mem = NULL; +} + diff --git a/thirdparty/libktx/lib/memstream.h b/thirdparty/libktx/lib/memstream.h new file mode 100644 index 0000000000..4ef8d59cd2 --- /dev/null +++ b/thirdparty/libktx/lib/memstream.h @@ -0,0 +1,43 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2010-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file + * @~English + * + * @brief Interface of ktxStream for memory. + * + * @author Maksim Kolesin + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ + +#ifndef MEMSTREAM_H +#define MEMSTREAM_H + +#include "ktx.h" + +/* + * Initialize a ktxStream to a ktxMemStream with internally + * allocated memory. Can be read or written. + */ +KTX_error_code ktxMemStream_construct(ktxStream* str, + ktx_bool_t freeOnDestruct); +/* + * Initialize a ktxStream to a read-only ktxMemStream reading + * from an array of bytes. + */ +KTX_error_code ktxMemStream_construct_ro(ktxStream* str, + const ktx_uint8_t* pBytes, + const ktx_size_t size); +void ktxMemStream_destruct(ktxStream* str); + +KTX_error_code ktxMemStream_getdata(ktxStream* str, ktx_uint8_t** ppBytes); + +#endif /* MEMSTREAM_H */ diff --git a/thirdparty/libktx/lib/swap.c b/thirdparty/libktx/lib/swap.c new file mode 100644 index 0000000000..3fdeb4f3a4 --- /dev/null +++ b/thirdparty/libktx/lib/swap.c @@ -0,0 +1,57 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* $Id: 02ea6de2d8db512ca3af08f48b98ab5f6c35e7e5 $ */ + +/* + * Copyright 2010-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <KHR/khrplatform.h> +#include "ktx.h" + +/* + * SwapEndian16: Swaps endianness in an array of 16-bit values + */ +void +_ktxSwapEndian16(khronos_uint16_t* pData16, ktx_size_t count) +{ + for (ktx_size_t i = 0; i < count; ++i) + { + khronos_uint16_t x = *pData16; + *pData16++ = (x << 8) | (x >> 8); + } +} + +/* + * SwapEndian32: Swaps endianness in an array of 32-bit values + */ +void +_ktxSwapEndian32(khronos_uint32_t* pData32, ktx_size_t count) +{ + for (ktx_size_t i = 0; i < count; ++i) + { + khronos_uint32_t x = *pData32; + *pData32++ = (x << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | (x >> 24); + } +} + +/* + * SwapEndian364: Swaps endianness in an array of 32-bit values + */ +void +_ktxSwapEndian64(khronos_uint64_t* pData64, ktx_size_t count) +{ + for (ktx_size_t i = 0; i < count; ++i) + { + khronos_uint64_t x = *pData64; + *pData64++ = (x << 56) | ((x & 0xFF00) << 40) | ((x & 0xFF0000) << 24) + | ((x & 0xFF000000) << 8 ) | ((x & 0xFF00000000) >> 8) + | ((x & 0xFF0000000000) >> 24) + | ((x & 0xFF000000000000) << 40) | (x >> 56); + } +} + + + diff --git a/thirdparty/libktx/lib/texture.c b/thirdparty/libktx/lib/texture.c new file mode 100644 index 0000000000..35619a3e73 --- /dev/null +++ b/thirdparty/libktx/lib/texture.c @@ -0,0 +1,911 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2018-2020 Mark Callow. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file writer.c + * @~English + * + * @brief ktxTexture implementation. + * + * @author Mark Callow, www.edgewise-consulting.com + */ + +#if defined(_WIN32) + #define _CRT_SECURE_NO_WARNINGS + #ifndef __cplusplus + #undef inline + #define inline __inline + #endif // __cplusplus +#endif + +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include "ktx.h" +#include "ktxint.h" +#include "formatsize.h" +#include "filestream.h" +#include "memstream.h" +#include "texture1.h" +#include "texture2.h" +#include "unused.h" + +ktx_size_t ktxTexture_GetDataSize(ktxTexture* This); + +static ktx_uint32_t padRow(ktx_uint32_t* rowBytes); + +/** + * @memberof ktxTexture @private + * @~English + * @brief Construct (initialize) a ktxTexture base class instance. + * + * @param[in] This pointer to a ktxTexture-sized block of memory to + * initialize. + * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with + * information describing the texture. + * @param[in] formatSize pointer to a ktxFormatSize giving size information + * about the texture's elements. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a + * valid OpenGL internal format value. + * @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2 + * or 3. + * @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in + * @p createInfo is 0. + * @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6. + * @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0. + * @exception KTX_INVALID_OPERATION + * The <tt>base{Width,Height,Depth}</tt> specified + * in @p createInfo are inconsistent with + * @c numDimensions. + * @exception KTX_INVALID_OPERATION + * @p createInfo is requesting a 3D array or + * 3D cubemap texture. + * @exception KTX_INVALID_OPERATION + * @p createInfo is requesting a cubemap with + * non-square or non-2D images. + * @exception KTX_INVALID_OPERATION + * @p createInfo is requesting more mip levels + * than needed for the specified + * <tt>base{Width,Height,Depth}</tt>. + * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture. + */ +KTX_error_code +ktxTexture_construct(ktxTexture* This, ktxTextureCreateInfo* createInfo, + ktxFormatSize* formatSize) +{ + DECLARE_PROTECTED(ktxTexture); + + memset(This, 0, sizeof(*This)); + This->_protected = (struct ktxTexture_protected*)malloc(sizeof(*prtctd)); + if (!This->_protected) + return KTX_OUT_OF_MEMORY; + prtctd = This->_protected; + memset(prtctd, 0, sizeof(*prtctd)); + memcpy(&prtctd->_formatSize, formatSize, sizeof(prtctd->_formatSize)); + + This->isCompressed = (formatSize->flags & KTX_FORMAT_SIZE_COMPRESSED_BIT); + + This->orientation.x = KTX_ORIENT_X_RIGHT; + This->orientation.y = KTX_ORIENT_Y_DOWN; + This->orientation.z = KTX_ORIENT_Z_OUT; + + /* Check texture dimensions. KTX files can store 8 types of textures: + * 1D, 2D, 3D, cube, and array variants of these. + */ + if (createInfo->numDimensions < 1 || createInfo->numDimensions > 3) + return KTX_INVALID_VALUE; + + if (createInfo->baseWidth == 0 || createInfo->baseHeight == 0 + || createInfo->baseDepth == 0) + return KTX_INVALID_VALUE; + + switch (createInfo->numDimensions) { + case 1: + if (createInfo->baseHeight > 1 || createInfo->baseDepth > 1) + return KTX_INVALID_OPERATION; + break; + + case 2: + if (createInfo->baseDepth > 1) + return KTX_INVALID_OPERATION; + break; + + case 3: + /* 3D array textures and 3D cubemaps are not supported by either + * OpenGL or Vulkan. + */ + if (createInfo->isArray || createInfo->numFaces != 1 + || createInfo->numLayers != 1) + return KTX_INVALID_OPERATION; + break; + } + This->numDimensions = createInfo->numDimensions; + This->baseWidth = createInfo->baseWidth; + This->baseDepth = createInfo->baseDepth; + This->baseHeight = createInfo->baseHeight; + + if (createInfo->numLayers == 0) + return KTX_INVALID_VALUE; + This->numLayers = createInfo->numLayers; + This->isArray = createInfo->isArray; + + if (createInfo->numFaces == 6) { + if (This->numDimensions != 2) { + /* cube map needs 2D faces */ + return KTX_INVALID_OPERATION; + } + if (createInfo->baseWidth != createInfo->baseHeight) { + /* cube maps require square images */ + return KTX_INVALID_OPERATION; + } + This->isCubemap = KTX_TRUE; + } else if (createInfo->numFaces != 1) { + /* numFaces must be either 1 or 6 */ + return KTX_INVALID_VALUE; + } + This->numFaces = createInfo->numFaces; + + /* Check number of mipmap levels */ + if (createInfo->numLevels == 0) + return KTX_INVALID_VALUE; + This->numLevels = createInfo->numLevels; + This->generateMipmaps = createInfo->generateMipmaps; + + if (createInfo->numLevels > 1) { + GLuint max_dim = MAX(MAX(createInfo->baseWidth, createInfo->baseHeight), + createInfo->baseDepth); + if (max_dim < ((GLuint)1 << (This->numLevels - 1))) + { + /* Can't have more mip levels than 1 + log2(max(width, height, depth)) */ + return KTX_INVALID_OPERATION; + } + } + + ktxHashList_Construct(&This->kvDataHead); + return KTX_SUCCESS; +} + +/** + * @memberof ktxTexture @private + * @~English + * @brief Construct (initialize) the part of a ktxTexture base class that is + * not related to the stream contents. + * + * @param[in] This pointer to a ktxTexture-sized block of memory to + * initialize. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + */ +KTX_error_code +ktxTexture_constructFromStream(ktxTexture* This, ktxStream* pStream, + ktxTextureCreateFlags createFlags) +{ + ktxStream* stream; + UNUSED(createFlags); // Reference to keep compiler happy. + + assert(This != NULL); + assert(pStream->data.mem != NULL); + assert(pStream->type == eStreamTypeFile + || pStream->type == eStreamTypeMemory + || pStream->type == eStreamTypeCustom); + + This->_protected = (struct ktxTexture_protected *) + malloc(sizeof(struct ktxTexture_protected)); + stream = ktxTexture_getStream(This); + // Copy stream info into struct for later use. + *stream = *pStream; + + This->orientation.x = KTX_ORIENT_X_RIGHT; + This->orientation.y = KTX_ORIENT_Y_DOWN; + This->orientation.z = KTX_ORIENT_Z_OUT; + + return KTX_SUCCESS; +} + + +/** + * @memberof ktxTexture @private + * @~English + * @brief Free the memory associated with the texture contents + * + * @param[in] This pointer to the ktxTextureInt whose texture contents are + * to be freed. + */ +void +ktxTexture_destruct(ktxTexture* This) +{ + ktxStream stream = *(ktxTexture_getStream(This)); + + if (stream.data.file != NULL) + stream.destruct(&stream); + if (This->kvDataHead != NULL) + ktxHashList_Destruct(&This->kvDataHead); + if (This->kvData != NULL) + free(This->kvData); + if (This->pData != NULL) + free(This->pData); + free(This->_protected); +} + + +/** + * @defgroup reader Reader + * @brief Read KTX-formatted data. + * @{ + */ + +typedef enum { KTX1, KTX2 } ktxFileType_; +typedef union { + KTX_header ktx; + KTX_header2 ktx2; +} ktxHeaderUnion_; + +/** + * @memberof ktxTexture @private + * @~English + * @brief Determine if stream data is KTX1 or KTX2. + * + * @param pStream pointer to the ktxStream to examine. + * @param pFileType pointer to a ktxFileType enum where the type of the data + * will be written. + * @param pHeader pointer to a ktxHeaderUnion where the header info. will be + * written. + */ +static KTX_error_code +ktxDetermineFileType_(ktxStream* pStream, ktxFileType_* pFileType, + ktxHeaderUnion_* pHeader) +{ + ktx_uint8_t ktx_ident_ref[12] = KTX_IDENTIFIER_REF; + ktx_uint8_t ktx2_ident_ref[12] = KTX2_IDENTIFIER_REF; + KTX_error_code result; + + assert(pStream != NULL && pFileType != NULL); + assert(pStream->data.mem != NULL); + assert(pStream->type == eStreamTypeFile + || pStream->type == eStreamTypeMemory + || pStream->type == eStreamTypeCustom); + + result = pStream->read(pStream, pHeader, sizeof(ktx2_ident_ref)); + if (result == KTX_SUCCESS) { +#if BIG_ENDIAN + // byte swap the heaader fields +#endif + // Compare identifier, is this a KTX or KTX2 file? + if (!memcmp(pHeader->ktx.identifier, ktx_ident_ref, 12)) { + *pFileType = KTX1; + } else if (!memcmp(pHeader->ktx2.identifier, ktx2_ident_ref, 12)) { + *pFileType = KTX2; + } else { + return KTX_UNKNOWN_FILE_FORMAT; + } + // Read rest of header. + if (*pFileType == KTX1) { + // Read rest of header. + result = pStream->read(pStream, &pHeader->ktx.endianness, + KTX_HEADER_SIZE - sizeof(ktx_ident_ref)); + } else { + result = pStream->read(pStream, &pHeader->ktx2.vkFormat, + KTX2_HEADER_SIZE - sizeof(ktx2_ident_ref)); + } + } + return result; +} + +/** + * @memberof ktxTexture + * @~English + * @brief Construct (initialize) a ktx1 or ktx2 texture according to the stream + * data. + * + * @copydetails ktxTexture_CreateFromStdioStream + */ +KTX_error_code +ktxTexture_CreateFromStream(ktxStream* pStream, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex) +{ + ktxHeaderUnion_ header; + ktxFileType_ fileType; + KTX_error_code result; + ktxTexture* tex; + + result = ktxDetermineFileType_(pStream, &fileType, &header); + if (result != KTX_SUCCESS) + return result; + + if (fileType == KTX1) { + ktxTexture1* tex1 = (ktxTexture1*)malloc(sizeof(ktxTexture1)); + if (tex1 == NULL) + return KTX_OUT_OF_MEMORY; + memset(tex1, 0, sizeof(ktxTexture1)); + result = ktxTexture1_constructFromStreamAndHeader(tex1, pStream, + &header.ktx, + createFlags); + tex = ktxTexture(tex1); + } else { + ktxTexture2* tex2 = (ktxTexture2*)malloc(sizeof(ktxTexture2)); + if (tex2 == NULL) + return KTX_OUT_OF_MEMORY; + memset(tex2, 0, sizeof(ktxTexture2)); + result = ktxTexture2_constructFromStreamAndHeader(tex2, pStream, + &header.ktx2, + createFlags); + tex = ktxTexture(tex2); + } + + if (result == KTX_SUCCESS) + *newTex = (ktxTexture*)tex; + else { + free(tex); + *newTex = NULL; + } + return result; +} + +/** + * @memberof ktxTexture + * @~English + * @brief Create a ktxTexture1 or ktxTexture2 from a stdio stream according + * to the stream data. + * + * @copydetails ktxTexture1_CreateFromStdioStream() + */ +KTX_error_code +ktxTexture_CreateFromStdioStream(FILE* stdioStream, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex) +{ + ktxStream stream; + KTX_error_code result; + + if (stdioStream == NULL || newTex == NULL) + return KTX_INVALID_VALUE; + + result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE); + if (result == KTX_SUCCESS) { + result = ktxTexture_CreateFromStream(&stream, createFlags, newTex); + } + return result; +} + +/** + * @memberof ktxTexture + * @~English + * @brief Create a ktxTexture1 or ktxTexture2 from a named KTX file according + * to the file contents. + * + * The address of a newly created ktxTexture reflecting the contents of the + * file is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture. + * + * @param[in] filename pointer to a char array containing the file name. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + + * @exception KTX_FILE_OPEN_FAILED The file could not be opened. + * @exception KTX_INVALID_VALUE @p filename is @c NULL. + * + * For other exceptions, see ktxTexture_CreateFromStdioStream(). + */ +KTX_error_code +ktxTexture_CreateFromNamedFile(const char* const filename, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex) +{ + KTX_error_code result; + ktxStream stream; + FILE* file; + + if (filename == NULL || newTex == NULL) + return KTX_INVALID_VALUE; + + file = fopen(filename, "rb"); + if (!file) + return KTX_FILE_OPEN_FAILED; + + result = ktxFileStream_construct(&stream, file, KTX_TRUE); + if (result == KTX_SUCCESS) { + result = ktxTexture_CreateFromStream(&stream, createFlags, newTex); + } + return result; +} + +/** + * @memberof ktxTexture + * @~English + * @brief Create a ktxTexture1 or ktxTexture2 from KTX-formatted data in memory + * according to the data contents. + * + * The address of a newly created ktxTexture reflecting the contents of the + * serialized KTX data is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture. + * + * @param[in] bytes pointer to the memory containing the serialized KTX data. + * @param[in] size length of the KTX data in bytes. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0. + * + * For other exceptions, see ktxTexture_CreateFromStdioStream(). + */ +KTX_error_code +ktxTexture_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex) +{ + KTX_error_code result; + ktxStream stream; + + if (bytes == NULL || newTex == NULL || size == 0) + return KTX_INVALID_VALUE; + + result = ktxMemStream_construct_ro(&stream, bytes, size); + if (result == KTX_SUCCESS) { + result = ktxTexture_CreateFromStream(&stream, createFlags, newTex); + } + return result;} + + +/** + * @memberof ktxTexture + * @~English + * @brief Return a pointer to the texture image data. + * + * @param[in] This pointer to the ktxTexture object of interest. + */ +ktx_uint8_t* +ktxTexture_GetData(ktxTexture* This) +{ + return This->pData; +} + +/** + * @memberof ktxTexture + * @~English + * @brief Return the total size of the texture image data in bytes. + * + * For a ktxTexture2 with supercompressionScheme != KTX_SS_NONE this will + * return the deflated size of the data. + * + * @param[in] This pointer to the ktxTexture object of interest. + */ +ktx_size_t +ktxTexture_GetDataSize(ktxTexture* This) +{ + assert(This != NULL); + return This->dataSize; +} + +/** + * @memberof ktxTexture + * @~English + * @brief Return the size in bytes of an elements of a texture's + * images. + * + * For uncompressed textures an element is one texel. For compressed + * textures it is one block. + * + * @param[in] This pointer to the ktxTexture object of interest. + */ +ktx_uint32_t +ktxTexture_GetElementSize(ktxTexture* This) +{ + assert (This != NULL); + + return (This->_protected->_formatSize.blockSizeInBits / 8); +} + +/** + * @memberof ktxTexture @private + * @~English + * @brief Calculate & return the size in bytes of an image at the specified + * mip level. + * + * For arrays, this is the size of layer, for cubemaps, the size of a face + * and for 3D textures, the size of a depth slice. + * + * The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level level of interest. + * @param[in] fv enum specifying format version for which to calculate + * image size. + */ +ktx_size_t +ktxTexture_calcImageSize(ktxTexture* This, ktx_uint32_t level, + ktxFormatVersionEnum fv) +{ + DECLARE_PROTECTED(ktxTexture); + struct blockCount { + ktx_uint32_t x, y; + } blockCount; + ktx_uint32_t blockSizeInBytes; + ktx_uint32_t rowBytes; + + assert (This != NULL); + + float levelWidth = (float)(This->baseWidth >> level); + float levelHeight = (float)(This->baseHeight >> level); + // Round up to next whole block. We can't use KTX_PADN because some of + // the block sizes are not powers of 2. + blockCount.x + = (ktx_uint32_t)ceilf(levelWidth / prtctd->_formatSize.blockWidth); + blockCount.y + = (ktx_uint32_t)ceilf(levelHeight / prtctd->_formatSize.blockHeight); + blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x); + blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y); + + blockSizeInBytes = prtctd->_formatSize.blockSizeInBits / 8; + + if (prtctd->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT) { + assert(This->isCompressed); + return blockCount.x * blockCount.y * blockSizeInBytes; + } else { + assert(prtctd->_formatSize.blockWidth == 1U + && prtctd->_formatSize.blockHeight == 1U + && prtctd->_formatSize.blockDepth == 1U); + rowBytes = blockCount.x * blockSizeInBytes; + if (fv == KTX_FORMAT_VERSION_ONE) + (void)padRow(&rowBytes); + return rowBytes * blockCount.y; + } +} + +/** + * @memberof ktxTexture + * @~English + * @brief Iterate over the levels or faces in a ktxTexture object. + * + * Blocks of image data are passed to an application-supplied callback + * function. This is not a strict per-image iteration. Rather it reflects how + * OpenGL needs the images. For most textures the block of data includes all + * images of a mip level which implies all layers of an array. However, for + * non-array cube map textures the block is a single face of the mip level, + * i.e the callback is called once for each face. + * + * This function works even if @p This->pData == 0 so it can be used to + * obtain offsets and sizes for each level by callers who have loaded the data + * externally. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in,out] iterCb the address of a callback function which is called + * with the data for each image block. + * @param[in,out] userdata the address of application-specific data which is + * passed to the callback along with the image data. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. The + * following are returned directly by this function. @p iterCb may + * return these for other causes or may return additional errors. + * + * @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not + * decreasing + * @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL. + * + */ +KTX_error_code +ktxTexture_IterateLevelFaces(ktxTexture* This, PFNKTXITERCB iterCb, + void* userdata) +{ + ktx_uint32_t miplevel; + KTX_error_code result = KTX_SUCCESS; + + if (This == NULL) + return KTX_INVALID_VALUE; + + if (iterCb == NULL) + return KTX_INVALID_VALUE; + + for (miplevel = 0; miplevel < This->numLevels; ++miplevel) + { + ktx_uint32_t faceLodSize; + ktx_uint32_t face; + ktx_uint32_t innerIterations; + GLsizei width, height, depth; + + /* Array textures have the same number of layers at each mip level. */ + width = MAX(1, This->baseWidth >> miplevel); + height = MAX(1, This->baseHeight >> miplevel); + depth = MAX(1, This->baseDepth >> miplevel); + + faceLodSize = (ktx_uint32_t)ktxTexture_calcFaceLodSize( + This, miplevel); + + /* All array layers are passed in a group because that is how + * GL & Vulkan need them. Hence no + * for (layer = 0; layer < This->numLayers) + */ + if (This->isCubemap && !This->isArray) + innerIterations = This->numFaces; + else + innerIterations = 1; + for (face = 0; face < innerIterations; ++face) + { + /* And all z_slices are also passed as a group hence no + * for (slice = 0; slice < This->depth) + */ + ktx_size_t offset; + + ktxTexture_GetImageOffset(This, miplevel, 0, face, &offset); + result = iterCb(miplevel, face, + width, height, depth, + faceLodSize, This->pData + offset, userdata); + + if (result != KTX_SUCCESS) + break; + } + } + + return result; +} + +/** + * @internal + * @brief Calculate and apply the padding needed to comply with + * KTX_GL_UNPACK_ALIGNMENT. + * + * For uncompressed textures, KTX format specifies KTX_GL_UNPACK_ALIGNMENT = 4. + * + * @param[in,out] rowBytes pointer to variable containing the packed no. of + * bytes in a row. The no. of bytes after padding + * is written into this location. + * @return the no. of bytes of padding. + */ +static ktx_uint32_t +padRow(ktx_uint32_t* rowBytes) +{ + ktx_uint32_t rowPadding; + + assert (rowBytes != NULL); + + rowPadding = _KTX_PAD_UNPACK_ALIGN_LEN(*rowBytes); + *rowBytes += rowPadding; + return rowPadding; +} + +/** + * @memberof ktxTexture @private + * @~English + * @brief Calculate the size of an array layer at the specified mip level. + * + * The size of a layer is the size of an image * either the number of faces + * or the number of depth slices. This is the size of a layer as needed to + * find the offset within the array of images of a level and layer so the size + * reflects any @c cubePadding. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level level whose layer size to return. + * + * @return the layer size in bytes. + */ +ktx_size_t +ktxTexture_layerSize(ktxTexture* This, ktx_uint32_t level, + ktxFormatVersionEnum fv) +{ + /* + * As there are no 3D cubemaps, the image's z block count will always be + * 1 for cubemaps and numFaces will always be 1 for 3D textures so the + * multiply is safe. 3D cubemaps, if they existed, would require + * imageSize * (blockCount.z + This->numFaces); + */ + DECLARE_PROTECTED(ktxTexture); + ktx_uint32_t blockCountZ; + ktx_size_t imageSize, layerSize; + + assert (This != NULL); + + blockCountZ = MAX(1, (This->baseDepth / prtctd->_formatSize.blockDepth) >> level); + imageSize = ktxTexture_calcImageSize(This, level, fv); + layerSize = imageSize * blockCountZ; + if (fv == KTX_FORMAT_VERSION_ONE && KTX_GL_UNPACK_ALIGNMENT != 4) { + if (This->isCubemap && !This->isArray) { + /* cubePadding. NOTE: this adds padding after the last face too. */ + layerSize += _KTX_PAD4(layerSize); + } + } + return layerSize * This->numFaces; +} + +/** + * @memberof ktxTexture @private + * @~English + * @brief Calculate the size of the specified mip level. + * + * The size of a level is the size of a layer * the number of layers. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level level whose layer size to return. + * + * @return the level size in bytes. + */ +ktx_size_t +ktxTexture_calcLevelSize(ktxTexture* This, ktx_uint32_t level, + ktxFormatVersionEnum fv) +{ + assert (This != NULL); + assert (level < This->numLevels); + return ktxTexture_layerSize(This, level, fv) * This->numLayers; +} + +/** + * @memberof ktxTexture @private + * @~English + * @brief Calculate the faceLodSize of the specified mip level. + * + * The faceLodSize of a level for most textures is the size of a level. For + * non-array cube map textures is the size of a face. This is the size that + * must be provided to OpenGL when uploading textures. Faces get uploaded 1 + * at a time while all layers of an array or all slices of a 3D texture are + * uploaded together. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level level whose layer size to return. + * + * @return the faceLodSize size in bytes. + */ +ktx_size_t +ktxTexture_doCalcFaceLodSize(ktxTexture* This, ktx_uint32_t level, + ktxFormatVersionEnum fv) +{ + /* + * For non-array cubemaps this is the size of a face. For everything + * else it is the size of the level. + */ + if (This->isCubemap && !This->isArray) + return ktxTexture_calcImageSize(This, level, fv); + else + return ktxTexture_calcLevelSize(This, level, fv); +} + + +/** + * @memberof ktxTexture @private + * @~English + * @brief Return the number of bytes needed to store all the image data for + * a ktxTexture. + * + * The caclulated size does not include space for storing the @c imageSize + * fields of each mip level. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] fv enum specifying format version for which to calculate + * image size. + * + * @return the data size in bytes. + */ +ktx_size_t +ktxTexture_calcDataSizeTexture(ktxTexture* This) +{ + assert (This != NULL); + return ktxTexture_calcDataSizeLevels(This, This->numLevels); +} + +/** + * @memberof ktxTexture @private + * @~English + * @brief Get information about rows of an uncompresssed texture image at a + * specified level. + * + * For an image at @p level of a ktxTexture provide the number of rows, the + * packed (unpadded) number of bytes in a row and the padding necessary to + * comply with KTX_GL_UNPACK_ALIGNMENT. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level level of interest. + * @param[in,out] numRows pointer to location to store the number of rows. + * @param[in,out] pRowLengthBytes pointer to location to store number of bytes + * in a row. + * @param[in.out] pRowPadding pointer to location to store the number of bytes + * of padding. + */ +void +ktxTexture_rowInfo(ktxTexture* This, ktx_uint32_t level, + ktx_uint32_t* numRows, ktx_uint32_t* pRowLengthBytes, + ktx_uint32_t* pRowPadding) +{ + DECLARE_PROTECTED(ktxTexture); + struct blockCount { + ktx_uint32_t x; + } blockCount; + + assert (This != NULL); + + assert(!This->isCompressed); + assert(prtctd->_formatSize.blockWidth == 1U + && prtctd->_formatSize.blockHeight == 1U + && prtctd->_formatSize.blockDepth == 1U); + + blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth) >> level); + *numRows = MAX(1, (This->baseHeight / prtctd->_formatSize.blockHeight) >> level); + + *pRowLengthBytes = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8; + *pRowPadding = padRow(pRowLengthBytes); +} + +/** + * @memberof ktxTexture + * @~English + * @brief Return pitch betweeb rows of a texture image level in bytes. + * + * For uncompressed textures the pitch is the number of bytes between + * rows of texels. For compressed textures it is the number of bytes + * between rows of blocks. The value is padded to GL_UNPACK_ALIGNMENT, + * if necessary. For all currently known compressed formats padding + * will not be necessary. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level level of interest. + * + * @return the row pitch in bytes. + */ + ktx_uint32_t + ktxTexture_GetRowPitch(ktxTexture* This, ktx_uint32_t level) + { + DECLARE_PROTECTED(ktxTexture) + struct blockCount { + ktx_uint32_t x; + } blockCount; + ktx_uint32_t pitch; + + blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth) >> level); + pitch = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8; + (void)padRow(&pitch); + + return pitch; + } + +/** + * @memberof ktxTexture @private + * @~English + * @brief Query if a ktxTexture has an active stream. + * + * Tests if a ktxTexture has unread image data. The internal stream is closed + * once all the images have been read. + * + * @param[in] This pointer to the ktxTexture object of interest. + * + * @return KTX_TRUE if there is an active stream, KTX_FALSE otherwise. + */ +ktx_bool_t +ktxTexture_isActiveStream(ktxTexture* This) +{ + assert(This != NULL); + ktxStream* stream = ktxTexture_getStream(This); + return stream->data.file != NULL; +} + +/** @} */ + diff --git a/thirdparty/libktx/lib/texture.h b/thirdparty/libktx/lib/texture.h new file mode 100644 index 0000000000..e415a09d6a --- /dev/null +++ b/thirdparty/libktx/lib/texture.h @@ -0,0 +1,107 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab textwidth=70: */ + +/* + * Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file texture.h + * @~English + * + * @brief Declare internal ktxTexture functions for sharing between + * compilation units. + * + * These functions are private and should not be used outside the library. + */ + +#ifndef _TEXTURE_H_ +#define _TEXTURE_H_ + +#include "ktx.h" +#include "formatsize.h" + +#define DECLARE_PRIVATE(class) class ## _private* private = This->_private +#define DECLARE_PROTECTED(class) class ## _protected* prtctd = This->_protected; + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + KTX_FORMAT_VERSION_ONE = 1, + KTX_FORMAT_VERSION_TWO = 2 +} ktxFormatVersionEnum; + +typedef ktx_size_t (* PFNCALCDATASIZELEVELS)(ktxTexture* This, + ktx_uint32_t levels); +typedef ktx_size_t (* PFNCALCFACELODSIZE)(ktxTexture* This, ktx_uint32_t level); +typedef ktx_size_t (* PFNCALCLEVELOFFSET)(ktxTexture* This, ktx_uint32_t level); +typedef struct ktxTexture_vtblInt { + PFNCALCDATASIZELEVELS calcDataSizeLevels; + PFNCALCFACELODSIZE calcFaceLodSize; + PFNCALCLEVELOFFSET calcLevelOffset; +} ktxTexture_vtblInt; + +#define ktxTexture_calcDataSizeLevels(This, levels) \ + This->_protected->_vtbl.calcDataSizeLevels(This, levels); +#define ktxTexture_calcFaceLodSize(This, level) \ + This->_protected->_vtbl.calcFaceLodSize(This, level); +#define ktxTexture_calcLevelOffset(This, level) \ + This->_protected->_vtbl.calcLevelOffset(This, level); + +/** + * @memberof ktxTexture + * @~English + * + * @brief protected members of ktxTexture. + */ +typedef struct ktxTexture_protected { + ktxTexture_vtblInt _vtbl; + ktxFormatSize _formatSize; + ktx_uint32_t _typeSize; + ktxStream _stream; +} ktxTexture_protected; + +#define ktxTexture_getStream(t) ((ktxStream*)(&(t)->_protected->_stream)) +#define ktxTexture1_getStream(t1) ktxTexture_getStream((ktxTexture*)t1) +#define ktxTexture2_getStream(t2) ktxTexture_getStream((ktxTexture*)t2) + +KTX_error_code +ktxTexture_iterateLoadedImages(ktxTexture* This, PFNKTXITERCB iterCb, + void* userdata); +KTX_error_code +ktxTexture_iterateSourceImages(ktxTexture* This, PFNKTXITERCB iterCb, + void* userdata); + +ktx_size_t ktxTexture_calcDataSizeTexture(ktxTexture* This); +ktx_size_t ktxTexture_calcImageSize(ktxTexture* This, ktx_uint32_t level, + ktxFormatVersionEnum fv); +ktx_bool_t ktxTexture_isActiveStream(ktxTexture* This); +ktx_size_t ktxTexture_calcLevelSize(ktxTexture* This, ktx_uint32_t level, + ktxFormatVersionEnum fv); +ktx_size_t ktxTexture_doCalcFaceLodSize(ktxTexture* This, ktx_uint32_t level, + ktxFormatVersionEnum fv); +ktx_size_t ktxTexture_layerSize(ktxTexture* This, ktx_uint32_t level, + ktxFormatVersionEnum fv); +void ktxTexture_rowInfo(ktxTexture* This, ktx_uint32_t level, + ktx_uint32_t* numRows, ktx_uint32_t* rowBytes, + ktx_uint32_t* rowPadding); +KTX_error_code +ktxTexture_construct(ktxTexture* This, ktxTextureCreateInfo* createInfo, + ktxFormatSize* formatSize); + +KTX_error_code +ktxTexture_constructFromStream(ktxTexture* This, ktxStream* pStream, + ktxTextureCreateFlags createFlags); + +void +ktxTexture_destruct(ktxTexture* This); + +#ifdef __cplusplus +} +#endif + +#endif /* _TEXTURE_H_ */ diff --git a/thirdparty/libktx/lib/texture1.c b/thirdparty/libktx/lib/texture1.c new file mode 100644 index 0000000000..8420f402b2 --- /dev/null +++ b/thirdparty/libktx/lib/texture1.c @@ -0,0 +1,1459 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file texture2.c + * @~English + * + * @brief ktxTexture1 implementation. Support for KTX format. + * + * @author Mark Callow, www.edgewise-consulting.com + */ + +#if defined(_WIN32) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include <stdlib.h> +#include <string.h> + +#include "dfdutils/dfd.h" +#include "ktx.h" +#include "ktxint.h" +#include "filestream.h" +#include "memstream.h" +#include "texture1.h" +#include "unused.h" +#include "gl_format.h" + +typedef struct ktxTexture1_private { + ktx_bool_t _needSwap; +} ktxTexture1_private; + +struct ktxTexture_vtbl ktxTexture1_vtbl; +struct ktxTexture_vtblInt ktxTexture1_vtblInt; + +static KTX_error_code +ktxTexture1_constructCommon(ktxTexture1* This) +{ + assert(This != NULL); + + This->classId = ktxTexture1_c; + This->vtbl = &ktxTexture1_vtbl; + This->_protected->_vtbl = ktxTexture1_vtblInt; + This->_private = (ktxTexture1_private*)malloc(sizeof(ktxTexture1_private)); + if (This->_private == NULL) { + return KTX_OUT_OF_MEMORY; + } + memset(This->_private, 0, sizeof(*This->_private)); + + return KTX_SUCCESS; +} + +/** + * @memberof ktxTexture1 @private + * @copydoc ktxTexture2_construct + */ +static KTX_error_code +ktxTexture1_construct(ktxTexture1* This, ktxTextureCreateInfo* createInfo, + ktxTextureCreateStorageEnum storageAllocation) +{ + ktxTexture_protected* prtctd; + ktxFormatSize formatSize; + GLuint typeSize; + GLenum glFormat; + KTX_error_code result; + + memset(This, 0, sizeof(*This)); + + This->glInternalformat = createInfo->glInternalformat; + glGetFormatSize(This->glInternalformat, &formatSize); + if (formatSize.blockSizeInBits == 0) { + // Most likely a deprecated legacy format. + return KTX_UNSUPPORTED_TEXTURE_TYPE; + } + glFormat= glGetFormatFromInternalFormat(createInfo->glInternalformat); + if (glFormat == GL_INVALID_VALUE) { + return KTX_INVALID_VALUE; + } + result = ktxTexture_construct(ktxTexture(This), createInfo, &formatSize); + if (result != KTX_SUCCESS) + return result; + + result = ktxTexture1_constructCommon(This); + if (result != KTX_SUCCESS) + return result; + prtctd = This->_protected; + + This->isCompressed + = (formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT); + if (This->isCompressed) { + This->glFormat = 0; + This->glBaseInternalformat = glFormat; + This->glType = 0; + prtctd->_typeSize = 1; + } else { + This->glBaseInternalformat = This->glFormat = glFormat; + This->glType + = glGetTypeFromInternalFormat(createInfo->glInternalformat); + if (This->glType == GL_INVALID_VALUE) { + result = KTX_INVALID_VALUE; + goto cleanup; + } + typeSize = glGetTypeSizeFromType(This->glType); + assert(typeSize != GL_INVALID_VALUE); + + /* Do some sanity checking */ + if (typeSize != 1 && + typeSize != 2 && + typeSize != 4) + { + /* Only 8, 16, and 32-bit types are supported for byte-swapping. + * See UNPACK_SWAP_BYTES & table 8.4 in the OpenGL 4.4 spec. + */ + result = KTX_INVALID_VALUE; + goto cleanup; + } + prtctd->_typeSize = typeSize; + } + + if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) { + This->dataSize + = ktxTexture_calcDataSizeTexture(ktxTexture(This)); + This->pData = malloc(This->dataSize); + if (This->pData == NULL) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + } + return result; + +cleanup: + ktxTexture1_destruct(This); + ktxTexture_destruct(ktxTexture(This)); + return result; +} + +/** + * @memberof ktxTexture1 @private + * @brief Construct a ktxTexture1 from a ktxStream reading from a KTX source. + * + * The KTX header, that must have been read prior to calling this, is passed + * to the function. + * + * The stream object is copied into the constructed ktxTexture1. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture1. + * + * @param[in] This pointer to a ktxTexture1-sized block of memory to + * initialize. + * @param[in] pStream pointer to the stream to read. + * @param[in] pHeader pointer to a KTX header that has already been read from + * the stream. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_DATA_ERROR + * Source data is inconsistent with the KTX + * specification. + * @exception KTX_FILE_READ_ERROR + * An error occurred while reading the source. + * @exception KTX_FILE_UNEXPECTED_EOF + * Not enough data in the source. + * @exception KTX_OUT_OF_MEMORY Not enough memory to load either the images or + * the key-value data. + * @exception KTX_UNKNOWN_FILE_FORMAT + * The source is not in KTX format. + * @exception KTX_UNSUPPORTED_TEXTURE_TYPE + * The source describes a texture type not + * supported by OpenGL or Vulkan, e.g, a 3D array. + */ +KTX_error_code +ktxTexture1_constructFromStreamAndHeader(ktxTexture1* This, ktxStream* pStream, + KTX_header* pHeader, + ktxTextureCreateFlags createFlags) +{ + ktxTexture1_private* private; + KTX_error_code result; + KTX_supplemental_info suppInfo; + ktxStream* stream; + ktx_off_t pos; + ktx_size_t size; + ktxFormatSize formatSize; + + assert(pHeader != NULL && pStream != NULL); + + memset(This, 0, sizeof(*This)); + result = ktxTexture_constructFromStream(ktxTexture(This), pStream, createFlags); + if (result != KTX_SUCCESS) + return result; + result = ktxTexture1_constructCommon(This); + if (result != KTX_SUCCESS) { + ktxTexture_destruct(ktxTexture(This)); + return result; + } + + private = This->_private; + stream = ktxTexture1_getStream(This); + + result = ktxCheckHeader1_(pHeader, &suppInfo); + if (result != KTX_SUCCESS) + goto cleanup; + + /* + * Initialize from pHeader info. + */ + This->glFormat = pHeader->glFormat; + This->glInternalformat = pHeader->glInternalformat; + This->glType = pHeader->glType; + glGetFormatSize(This->glInternalformat, &formatSize); + if (formatSize.blockSizeInBits == 0) { + // Most likely a deprecated legacy format. + result = KTX_UNSUPPORTED_TEXTURE_TYPE; + goto cleanup; + } + This->_protected->_formatSize = formatSize; + This->glBaseInternalformat = pHeader->glBaseInternalformat; + // Can these be done by a ktxTexture_constructFromStream? + This->numDimensions = suppInfo.textureDimension; + This->baseWidth = pHeader->pixelWidth; + assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4); + switch (suppInfo.textureDimension) { + case 1: + This->baseHeight = This->baseDepth = 1; + break; + case 2: + This->baseHeight = pHeader->pixelHeight; + This->baseDepth = 1; + break; + case 3: + This->baseHeight = pHeader->pixelHeight; + This->baseDepth = pHeader->pixelDepth; + break; + } + if (pHeader->numberOfArrayElements > 0) { + This->numLayers = pHeader->numberOfArrayElements; + This->isArray = KTX_TRUE; + } else { + This->numLayers = 1; + This->isArray = KTX_FALSE; + } + This->numFaces = pHeader->numberOfFaces; + if (pHeader->numberOfFaces == 6) + This->isCubemap = KTX_TRUE; + else + This->isCubemap = KTX_FALSE; + This->numLevels = pHeader->numberOfMipLevels; + This->isCompressed = suppInfo.compressed; + This->generateMipmaps = suppInfo.generateMipmaps; + if (pHeader->endianness == KTX_ENDIAN_REF_REV) + private->_needSwap = KTX_TRUE; + This->_protected->_typeSize = pHeader->glTypeSize; + + /* + * Make an empty hash list. + */ + ktxHashList_Construct(&This->kvDataHead); + /* + * Load KVData. + */ + if (pHeader->bytesOfKeyValueData > 0) { + if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) { + ktx_uint32_t kvdLen = pHeader->bytesOfKeyValueData; + ktx_uint8_t* pKvd; + + pKvd = malloc(kvdLen); + if (pKvd == NULL) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + + result = stream->read(stream, pKvd, kvdLen); + if (result != KTX_SUCCESS) + goto cleanup; + + if (private->_needSwap) { + /* Swap the counts inside the key & value data. */ + ktx_uint8_t* src = pKvd; + ktx_uint8_t* end = pKvd + kvdLen; + while (src < end) { + ktx_uint32_t* pKeyAndValueByteSize = (ktx_uint32_t*)src; + _ktxSwapEndian32(pKeyAndValueByteSize, 1); + src += _KTX_PAD4(*pKeyAndValueByteSize); + } + } + + if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) { + char* orientation; + ktx_uint32_t orientationLen; + + result = ktxHashList_Deserialize(&This->kvDataHead, + kvdLen, pKvd); + free(pKvd); + if (result != KTX_SUCCESS) { + goto cleanup; + } + + result = ktxHashList_FindValue(&This->kvDataHead, + KTX_ORIENTATION_KEY, + &orientationLen, + (void**)&orientation); + assert(result != KTX_INVALID_VALUE); + if (result == KTX_SUCCESS) { + ktx_uint32_t count; + char orient[4] = {0, 0, 0, 0}; + + count = sscanf(orientation, KTX_ORIENTATION3_FMT, + &orient[0], + &orient[1], + &orient[2]); + + if (count > This->numDimensions) { + // KTX 1 is less strict than KTX2 so there is a chance + // of having more dimensions than needed. + count = This->numDimensions; + } + switch (This->numDimensions) { + case 3: + This->orientation.z = orient[2]; + FALLTHROUGH; + case 2: + This->orientation.y = orient[1]; + FALLTHROUGH; + case 1: + This->orientation.x = orient[0]; + } + } + } else { + This->kvDataLen = kvdLen; + This->kvData = pKvd; + } + } else { + stream->skip(stream, pHeader->bytesOfKeyValueData); + } + } + + /* + * Get the size of the image data. + */ + result = stream->getsize(stream, &size); + if (result != KTX_SUCCESS) + goto cleanup; + + result = stream->getpos(stream, &pos); + if (result != KTX_SUCCESS) + goto cleanup; + + /* Remove space for faceLodSize fields */ + This->dataSize = size - pos - This->numLevels * sizeof(ktx_uint32_t); + + /* + * Load the images, if requested. + */ + if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) { + result = ktxTexture1_LoadImageData(This, NULL, 0); + } + if (result == KTX_SUCCESS) + return result; + +cleanup: + ktxTexture1_destruct(This); + return result; +} + +/** + * @memberof ktxTexture1 @private + * @brief Construct a ktxTexture1 from a ktxStream reading from a KTX source. + * + * The stream object is copied into the constructed ktxTexture1. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture1. + * + * @param[in] This pointer to a ktxTexture1-sized block of memory to + * initialize. + * @param[in] pStream pointer to the stream to read. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_READ_ERROR + * An error occurred while reading the source. + * + * For other exceptions see ktxTexture1_constructFromStreamAndHeader(). + */ +static KTX_error_code +ktxTexture1_constructFromStream(ktxTexture1* This, ktxStream* pStream, + ktxTextureCreateFlags createFlags) +{ + KTX_header header; + KTX_error_code result; + + // Read header. + result = pStream->read(pStream, &header, KTX_HEADER_SIZE); + if (result != KTX_SUCCESS) + return result; + + return ktxTexture1_constructFromStreamAndHeader(This, pStream, + &header, createFlags); +} + +/** + * @memberof ktxTexture1 @private + * @brief Construct a ktxTexture1 from a stdio stream reading from a KTX source. + * + * See ktxTextureInt_constructFromStream for details. + * + * @note Do not close the stdio stream until you are finished with the texture + * object. + * + * @param[in] This pointer to a ktxTextureInt-sized block of memory to + * initialize. + * @param[in] stdioStream a stdio FILE pointer opened on the source. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE Either @p stdiostream or @p This is null. + * + * For other exceptions, see ktxTexture_constructFromStream(). + */ +static KTX_error_code +ktxTexture1_constructFromStdioStream(ktxTexture1* This, FILE* stdioStream, + ktxTextureCreateFlags createFlags) +{ + ktxStream stream; + KTX_error_code result; + + if (stdioStream == NULL || This == NULL) + return KTX_INVALID_VALUE; + + result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE); + if (result == KTX_SUCCESS) + result = ktxTexture1_constructFromStream(This, &stream, createFlags); + return result; +} + +/** + * @memberof ktxTexture1 @private + * @brief Construct a ktxTexture1 from a named KTX file. + * + * See ktxTextureInt_constructFromStream for details. + * + * @param[in] This pointer to a ktxTextureInt-sized block of memory to + * initialize. + * @param[in] filename pointer to a char array containing the file name. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_OPEN_FAILED The file could not be opened. + * @exception KTX_INVALID_VALUE @p filename is @c NULL. + * + * For other exceptions, see ktxTexture_constructFromStream(). + */ +static KTX_error_code +ktxTexture1_constructFromNamedFile(ktxTexture1* This, + const char* const filename, + ktxTextureCreateFlags createFlags) +{ + FILE* file; + ktxStream stream; + KTX_error_code result; + + if (This == NULL || filename == NULL) + return KTX_INVALID_VALUE; + + file = fopen(filename, "rb"); + if (!file) + return KTX_FILE_OPEN_FAILED; + + result = ktxFileStream_construct(&stream, file, KTX_TRUE); + if (result == KTX_SUCCESS) + result = ktxTexture1_constructFromStream(This, &stream, createFlags); + + return result; +} + +/** + * @memberof ktxTexture1 @private + * @brief Construct a ktxTexture1 from KTX-formatted data in memory. + * + * See ktxTextureInt_constructFromStream for details. + * + * @param[in] This pointer to a ktxTextureInt-sized block of memory to + * initialize. + * @param[in] bytes pointer to the memory containing the serialized KTX data. + * @param[in] size length of the KTX data in bytes. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0. + * + * For other exceptions, see ktxTexture_constructFromStream(). + */ +static KTX_error_code +ktxTexture1_constructFromMemory(ktxTexture1* This, + const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags) +{ + ktxStream stream; + KTX_error_code result; + + if (bytes == NULL || size == 0) + return KTX_INVALID_VALUE; + + result = ktxMemStream_construct_ro(&stream, bytes, size); + if (result == KTX_SUCCESS) + result = ktxTexture1_constructFromStream(This, &stream, createFlags); + + return result; +} + +void +ktxTexture1_destruct(ktxTexture1* This) +{ + if (This->_private) free(This->_private); + ktxTexture_destruct(ktxTexture(This)); +} + +/** + * @defgroup reader Reader + * @brief Read KTX-formatted data. + * @{ + */ + +/** + * @memberof ktxTexture1 + * @ingroup writer + * @brief Create a new empty ktxTexture1. + * + * The address of the newly created ktxTexture1 is written to the location + * pointed at by @p newTex. + * + * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with + * information describing the texture. + * @param[in] storageAllocation + * enum indicating whether or not to allocate storage + * for the texture images. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a + * valid OpenGL internal format value. + * @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2 + * or 3. + * @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in + * @p createInfo is 0. + * @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6. + * @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0. + * @exception KTX_INVALID_OPERATION + * The <tt>base{Width,Height,Depth}</tt> specified + * in @p createInfo are inconsistent with + * @c numDimensions. + * @exception KTX_INVALID_OPERATION + * @p createInfo is requesting a 3D array or + * 3D cubemap texture. + * @exception KTX_INVALID_OPERATION + * @p createInfo is requesting a cubemap with + * non-square or non-2D images. + * @exception KTX_INVALID_OPERATION + * @p createInfo is requesting more mip levels + * than needed for the specified + * <tt>base{Width,Height,Depth}</tt>. + * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture's images. + */ +KTX_error_code +ktxTexture1_Create(ktxTextureCreateInfo* createInfo, + ktxTextureCreateStorageEnum storageAllocation, + ktxTexture1** newTex) +{ + KTX_error_code result; + + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture1_construct(tex, createInfo, storageAllocation); + if (result != KTX_SUCCESS) { + free(tex); + } else { + *newTex = tex; + } + return result; +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Create a ktxTexture1 from a stdio stream reading from a KTX source. + * + * The address of a newly created ktxTexture1 reflecting the contents of the + * stdio stream is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture1. + * + * @param[in] stdioStream stdio FILE pointer created from the desired file. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p newTex is @c NULL. + * @exception KTX_FILE_DATA_ERROR + * Source data is inconsistent with the KTX + * specification. + * @exception KTX_FILE_READ_ERROR + * An error occurred while reading the source. + * @exception KTX_FILE_UNEXPECTED_EOF + * Not enough data in the source. + * @exception KTX_OUT_OF_MEMORY Not enough memory to create the texture object, + * load the images or load the key-value data. + * @exception KTX_UNKNOWN_FILE_FORMAT + * The source is not in KTX format. + * @exception KTX_UNSUPPORTED_TEXTURE_TYPE + * The source describes a texture type not + * supported by OpenGL or Vulkan, e.g, a 3D array. + */ +KTX_error_code +ktxTexture1_CreateFromStdioStream(FILE* stdioStream, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex) +{ + KTX_error_code result; + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture1_constructFromStdioStream(tex, stdioStream, + createFlags); + if (result == KTX_SUCCESS) + *newTex = (ktxTexture1*)tex; + else { + free(tex); + *newTex = NULL; + } + return result; +} + +/* + * @memberof ktxTexture1 + * @~English + * @brief Create a ktxTexture1 from a named KTX file. + * + * The address of a newly created ktxTexture1 reflecting the contents of the + * file is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture1. + * + * @param[in] filename pointer to a char array containing the file name. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_OPEN_FAILED The file could not be opened. + * @exception KTX_INVALID_VALUE @p filename is @c NULL. + * + * For other exceptions, see ktxTexture_CreateFromStdioStream(). + */ +KTX_error_code +ktxTexture1_CreateFromNamedFile(const char* const filename, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex) +{ + KTX_error_code result; + + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture1_constructFromNamedFile(tex, filename, createFlags); + if (result == KTX_SUCCESS) + *newTex = (ktxTexture1*)tex; + else { + free(tex); + *newTex = NULL; + } + return result; +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Create a ktxTexture1 from KTX-formatted data in memory. + * + * The address of a newly created ktxTexture1 reflecting the contents of the + * serialized KTX data is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture1. + * + * @param[in] bytes pointer to the memory containing the serialized KTX data. + * @param[in] size length of the KTX data in bytes. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0. + * + * For other exceptions, see ktxTexture_CreateFromStdioStream(). + */ +KTX_error_code +ktxTexture1_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex) +{ + KTX_error_code result; + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture1_constructFromMemory(tex, bytes, size, + createFlags); + if (result == KTX_SUCCESS) + *newTex = (ktxTexture1*)tex; + else { + free(tex); + *newTex = NULL; + } + return result; +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Create a ktxTexture1 from KTX-formatted data from a `ktxStream`. + * + * The address of a newly created ktxTexture1 reflecting the contents of the + * serialized KTX data is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture1. + * + * @param[in] stream pointer to the stream to read KTX data from. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0. + * + * For other exceptions, see ktxTexture_CreateFromStdioStream(). + */ +KTX_error_code +ktxTexture1_CreateFromStream(ktxStream* stream, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex) +{ + KTX_error_code result; + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture1_constructFromStream(tex, stream, createFlags); + if (result == KTX_SUCCESS) + *newTex = (ktxTexture1*)tex; + else { + free(tex); + *newTex = NULL; + } + return result; +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Destroy a ktxTexture1 object. + * + * This frees the memory associated with the texture contents and the memory + * of the ktxTexture1 object. This does @e not delete any OpenGL or Vulkan + * texture objects created by ktxTexture1_GLUpload or ktxTexture1_VkUpload. + * + * @param[in] This pointer to the ktxTexture1 object to destroy + */ +void +ktxTexture1_Destroy(ktxTexture1* This) +{ + ktxTexture1_destruct(This); + free(This); +} + +/** + * @memberof ktxTexture @private + * @~English + * @brief Calculate the size of the image data for the specified number + * of levels. + * + * The data size is the sum of the sizes of each level up to the number + * specified and includes any @c mipPadding. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] levels number of levels whose data size to return. + * + * @return the data size in bytes. + */ +ktx_size_t +ktxTexture1_calcDataSizeLevels(ktxTexture1* This, ktx_uint32_t levels) +{ + ktx_uint32_t i; + ktx_size_t dataSize = 0; + + assert(This != NULL); + assert(levels <= This->numLevels); + for (i = 0; i < levels; i++) { + ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i, + KTX_FORMAT_VERSION_ONE); + /* mipPadding. NOTE: this adds padding after the last level too. */ + #if KTX_GL_UNPACK_ALIGNMENT != 4 + dataSize += _KTX_PAD4(levelSize); + #else + dataSize += levelSize; + #endif + } + return dataSize; +} + +/** + * @memberof ktxTexture1 @private + * @~English + * + * @copydoc ktxTexture::ktxTexture_doCalcFaceLodSize + */ +ktx_size_t +ktxTexture1_calcFaceLodSize(ktxTexture1* This, ktx_uint32_t level) +{ + return ktxTexture_doCalcFaceLodSize(ktxTexture(This), level, + KTX_FORMAT_VERSION_ONE); +} + +/** + * @memberof ktxTexture @private + * @~English + * @brief Return the offset of a level in bytes from the start of the image + * data in a ktxTexture. + * + * The caclulated size does not include space for storing the @c imageSize + * fields of each mip level. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level level whose offset to return. + * @param[in] fv enum specifying format version for which to calculate + * image size. + * + * @return the data size in bytes. + */ +ktx_size_t +ktxTexture1_calcLevelOffset(ktxTexture1* This, ktx_uint32_t level) +{ + assert (This != NULL); + assert (level < This->numLevels); + return ktxTexture1_calcDataSizeLevels(This, level); +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Find the offset of an image within a ktxTexture's image data. + * + * As there is no such thing as a 3D cubemap we make the 3rd location parameter + * do double duty. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level mip level of the image. + * @param[in] layer array layer of the image. + * @param[in] faceSlice cube map face or depth slice of the image. + * @param[in,out] pOffset pointer to location to store the offset. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_OPERATION + * @p level, @p layer or @p faceSlice exceed the + * dimensions of the texture. + * @exception KTX_INVALID_VALID @p This is NULL. + */ +KTX_error_code +ktxTexture1_GetImageOffset(ktxTexture1* This, ktx_uint32_t level, + ktx_uint32_t layer, ktx_uint32_t faceSlice, + ktx_size_t* pOffset) +{ + + if (This == NULL) + return KTX_INVALID_VALUE; + + if (level >= This->numLevels || layer >= This->numLayers) + return KTX_INVALID_OPERATION; + + if (This->isCubemap) { + if (faceSlice >= This->numFaces) + return KTX_INVALID_OPERATION; + } else { + ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level); + if (faceSlice >= maxSlice) + return KTX_INVALID_OPERATION; + } + + // Get the size of the data up to the start of the indexed level. + *pOffset = ktxTexture_calcDataSizeLevels(ktxTexture(This), level); + + // All layers, faces & slices within a level are the same size. + if (layer != 0) { + ktx_size_t layerSize; + layerSize = ktxTexture_layerSize(ktxTexture(This), level, + KTX_FORMAT_VERSION_ONE); + *pOffset += layer * layerSize; + } + if (faceSlice != 0) { + ktx_size_t imageSize; + imageSize = ktxTexture_GetImageSize(ktxTexture(This), level); +#if (KTX_GL_UNPACK_ALIGNMENT != 4) + if (This->isCubemap) + _KTX_PAD4(imageSize); // Account for cubePadding. +#endif + *pOffset += faceSlice * imageSize; + } + + return KTX_SUCCESS; +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Return the total size in bytes of the uncompressed data of a ktxTexture1. + * + * This always returns the value of @c This->dataSize. The function is provided for + * symmetry with ktxTexture2. + * + * @param[in] This pointer to the ktxTexture1 object of interest. + * @return The size of the data in the texture. + */ +ktx_size_t +ktxTexture1_GetDataSizeUncompressed(ktxTexture1* This) +{ + return This->dataSize; +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Calculate & return the size in bytes of an image at the specified + * mip level. + * + * For arrays, this is the size of layer, for cubemaps, the size of a face + * and for 3D textures, the size of a depth slice. + * + * The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT. + * + * @param[in] This pointer to the ktxTexture1 object of interest. + * @param[in] level level of interest. + */ +ktx_size_t +ktxTexture1_GetImageSize(ktxTexture1* This, ktx_uint32_t level) +{ + return ktxTexture_calcImageSize(ktxTexture(This), level, + KTX_FORMAT_VERSION_ONE); +} + +/** + * @memberof ktxTexture1 @private + * @~English + * @brief Return the size of the primitive type of a single color component + * + * @param[in] This pointer to the ktxTexture1 object of interest. + * + * @return the type size in bytes. + */ +ktx_uint32_t +ktxTexture1_glTypeSize(ktxTexture1* This) +{ + assert(This != NULL); + return This->_protected->_typeSize; +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Iterate over the mip levels in a ktxTexture1 object. + * + * This is almost identical to ktxTexture_IterateLevelFaces(). The difference is + * that the blocks of image data for non-array cube maps include all faces of + * a mip level. + * + * This function works even if @p This->pData == 0 so it can be used to + * obtain offsets and sizes for each level by callers who have loaded the data + * externally. + * + * @param[in] This handle of the 1 opened on the data. + * @param[in,out] iterCb the address of a callback function which is called + * with the data for each image block. + * @param[in,out] userdata the address of application-specific data which is + * passed to the callback along with the image data. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. The + * following are returned directly by this function. @p iterCb may + * return these for other causes or may return additional errors. + * + * @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not + * decreasing + * @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL. + * + */ +KTX_error_code +ktxTexture1_IterateLevels(ktxTexture1* This, PFNKTXITERCB iterCb, void* userdata) +{ + ktx_uint32_t miplevel; + KTX_error_code result = KTX_SUCCESS; + + if (This == NULL) + return KTX_INVALID_VALUE; + + if (iterCb == NULL) + return KTX_INVALID_VALUE; + + for (miplevel = 0; miplevel < This->numLevels; ++miplevel) + { + GLsizei width, height, depth; + ktx_uint32_t levelSize; + ktx_size_t offset; + + /* Array textures have the same number of layers at each mip level. */ + width = MAX(1, This->baseWidth >> miplevel); + height = MAX(1, This->baseHeight >> miplevel); + depth = MAX(1, This->baseDepth >> miplevel); + + levelSize = (ktx_uint32_t)ktxTexture_calcLevelSize(ktxTexture(This), + miplevel, + KTX_FORMAT_VERSION_ONE); + + /* All array layers are passed in a group because that is how + * GL & Vulkan need them. Hence no + * for (layer = 0; layer < This->numLayers) + */ + ktxTexture_GetImageOffset(ktxTexture(This), miplevel, 0, 0, &offset); + result = iterCb(miplevel, 0, width, height, depth, + levelSize, This->pData + offset, userdata); + if (result != KTX_SUCCESS) + break; + } + + return result; +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Iterate over the images in a ktxTexture1 object while loading the + * image data. + * + * This operates similarly to ktxTexture_IterateLevelFaces() except that it + * loads the images from the ktxTexture1's source to a temporary buffer + * while iterating. The callback function must copy the image data if it + * wishes to preserve it as the temporary buffer is reused for each level and + * is freed when this function exits. + * + * This function is helpful for reducing memory usage when uploading the data + * to a graphics API. + * + * @param[in] This pointer to the ktxTexture1 object of interest. + * @param[in,out] iterCb the address of a callback function which is called + * with the data for each image. + * @param[in,out] userdata the address of application-specific data which is + * passed to the callback along with the image data. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. The + * following are returned directly by this function. @p iterCb may + * return these for other causes or may return additional errors. + * + * @exception KTX_FILE_DATA_ERROR mip level sizes are increasing not + * decreasing + * @exception KTX_INVALID_OPERATION the ktxTexture1 was not created from a + * stream, i.e there is no data to load, or + * this ktxTexture1's images have already + * been loaded. + * @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL. + * @exception KTX_OUT_OF_MEMORY not enough memory to allocate a block to + * hold the base level image. + */ +KTX_error_code +ktxTexture1_IterateLoadLevelFaces(ktxTexture1* This, PFNKTXITERCB iterCb, + void* userdata) +{ + DECLARE_PRIVATE(ktxTexture1); + struct ktxTexture_protected* prtctd = This->_protected; + ktxStream* stream = (ktxStream *)&prtctd->_stream; + ktx_uint32_t dataSize = 0; + ktx_uint32_t miplevel; + KTX_error_code result = KTX_SUCCESS; + void* data = NULL; + + if (This == NULL) + return KTX_INVALID_VALUE; + + if (This->classId != ktxTexture1_c) + return KTX_INVALID_OPERATION; + + if (iterCb == NULL) + return KTX_INVALID_VALUE; + + if (prtctd->_stream.data.file == NULL) + // This Texture not created from a stream or images are already loaded. + return KTX_INVALID_OPERATION; + + for (miplevel = 0; miplevel < This->numLevels; ++miplevel) + { + ktx_uint32_t faceLodSize; + ktx_uint32_t faceLodSizePadded; + ktx_uint32_t face; + ktx_uint32_t innerIterations; + GLsizei width, height, depth; + + /* Array textures have the same number of layers at each mip level. */ + width = MAX(1, This->baseWidth >> miplevel); + height = MAX(1, This->baseHeight >> miplevel); + depth = MAX(1, This->baseDepth >> miplevel); + + result = stream->read(stream, &faceLodSize, sizeof(ktx_uint32_t)); + if (result != KTX_SUCCESS) { + goto cleanup; + } + if (private->_needSwap) { + _ktxSwapEndian32(&faceLodSize, 1); + } +#if (KTX_GL_UNPACK_ALIGNMENT != 4) + faceLodSizePadded = _KTX_PAD4(faceLodSize); +#else + faceLodSizePadded = faceLodSize; +#endif + if (!data) { + /* allocate memory sufficient for the base miplevel */ + data = malloc(faceLodSizePadded); + if (!data) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + dataSize = faceLodSizePadded; + } + else if (dataSize < faceLodSizePadded) { + /* subsequent miplevels cannot be larger than the base miplevel */ + result = KTX_FILE_DATA_ERROR; + goto cleanup; + } + + /* All array layers are passed in a group because that is how + * GL & Vulkan need them. Hence no + * for (layer = 0; layer < This->numLayers) + */ + if (This->isCubemap && !This->isArray) + innerIterations = This->numFaces; + else + innerIterations = 1; + for (face = 0; face < innerIterations; ++face) + { + /* And all z_slices are also passed as a group hence no + * for (z_slice = 0; z_slice < This->depth) + */ + result = stream->read(stream, data, faceLodSizePadded); + if (result != KTX_SUCCESS) { + goto cleanup; + } + + /* Perform endianness conversion on texture data */ + if (private->_needSwap) { + if (prtctd->_typeSize == 2) + _ktxSwapEndian16((ktx_uint16_t*)data, faceLodSize / 2); + else if (prtctd->_typeSize == 4) + _ktxSwapEndian32((ktx_uint32_t*)data, faceLodSize / 4); + } + + result = iterCb(miplevel, face, + width, height, depth, + faceLodSize, data, userdata); + } + } + +cleanup: + free(data); + // No further need for this. + stream->destruct(stream); + + return result; +} + +/** + * @memberof ktxTexture1 + * @~English + * @brief Load all the image data from the ktxTexture1's source. + * + * The data is loaded into the provided buffer or to an internally allocated + * buffer, if @p pBuffer is @c NULL. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] pBuffer pointer to the buffer in which to load the image data. + * @param[in] bufSize size of the buffer pointed at by @p pBuffer. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p This is NULL. + * @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size. + * @exception KTX_INVALID_OPERATION + * The data has already been loaded or the + * ktxTexture was not created from a KTX source. + * @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data. + */ +KTX_error_code +ktxTexture1_LoadImageData(ktxTexture1* This, + ktx_uint8_t* pBuffer, ktx_size_t bufSize) +{ + DECLARE_PROTECTED(ktxTexture); + DECLARE_PRIVATE(ktxTexture1); + ktx_uint32_t miplevel; + ktx_uint8_t* pDest; + KTX_error_code result = KTX_SUCCESS; + + if (This == NULL) + return KTX_INVALID_VALUE; + + if (prtctd->_stream.data.file == NULL) + // This Texture not created from a stream or images already loaded; + return KTX_INVALID_OPERATION; + + if (pBuffer == NULL) { + This->pData = malloc(This->dataSize); + if (This->pData == NULL) + return KTX_OUT_OF_MEMORY; + pDest = This->pData; + } else if (bufSize < This->dataSize) { + return KTX_INVALID_VALUE; + } else { + pDest = pBuffer; + } + + // Need to loop through for correct byte swapping + for (miplevel = 0; miplevel < This->numLevels; ++miplevel) + { + ktx_uint32_t faceLodSize; + ktx_uint32_t faceLodSizePadded; + ktx_uint32_t face; + ktx_uint32_t innerIterations; + + result = prtctd->_stream.read(&prtctd->_stream, &faceLodSize, + sizeof(ktx_uint32_t)); + if (result != KTX_SUCCESS) { + goto cleanup; + } + if (private->_needSwap) { + _ktxSwapEndian32(&faceLodSize, 1); + } +#if (KTX_GL_UNPACK_ALIGNMENT != 4) + faceLodSizePadded = _KTX_PAD4(faceLodSize); +#else + faceLodSizePadded = faceLodSize; +#endif + + if (This->isCubemap && !This->isArray) + innerIterations = This->numFaces; + else + innerIterations = 1; + for (face = 0; face < innerIterations; ++face) + { + result = prtctd->_stream.read(&prtctd->_stream, pDest, + faceLodSizePadded); + if (result != KTX_SUCCESS) { + goto cleanup; + } + + /* Perform endianness conversion on texture data */ + if (private->_needSwap) { + if (prtctd->_typeSize == 2) + _ktxSwapEndian16((ktx_uint16_t*)pDest, faceLodSize / 2); + else if (prtctd->_typeSize == 4) + _ktxSwapEndian32((ktx_uint32_t*)pDest, faceLodSize / 4); + } + + pDest += faceLodSizePadded; + } + } + +cleanup: + // No further need for This-> + prtctd->_stream.destruct(&prtctd->_stream); + return result; +} + +ktx_bool_t +ktxTexture1_NeedsTranscoding(ktxTexture1* This) +{ + UNUSED(This); + return KTX_FALSE; +} + +#if !KTX_FEATURE_WRITE + +/* + * Stubs for writer functions that return a proper error code + */ + +KTX_error_code +ktxTexture1_SetImageFromMemory(ktxTexture1* This, ktx_uint32_t level, + ktx_uint32_t layer, ktx_uint32_t faceSlice, + const ktx_uint8_t* src, ktx_size_t srcSize) +{ + UNUSED(This); + UNUSED(level); + UNUSED(layer); + UNUSED(faceSlice); + UNUSED(src); + UNUSED(srcSize); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture1_SetImageFromStdioStream(ktxTexture1* This, ktx_uint32_t level, + ktx_uint32_t layer, ktx_uint32_t faceSlice, + FILE* src, ktx_size_t srcSize) +{ + UNUSED(This); + UNUSED(level); + UNUSED(layer); + UNUSED(faceSlice); + UNUSED(src); + UNUSED(srcSize); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture1_WriteToStdioStream(ktxTexture1* This, FILE* dstsstr) +{ + UNUSED(This); + UNUSED(dstsstr); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture1_WriteToNamedFile(ktxTexture1* This, const char* const dstname) +{ + UNUSED(This); + UNUSED(dstname); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture1_WriteToMemory(ktxTexture1* This, + ktx_uint8_t** ppDstBytes, ktx_size_t* pSize) +{ + UNUSED(This); + UNUSED(ppDstBytes); + UNUSED(pSize); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture1_WriteToStream(ktxTexture1* This, + ktxStream* dststr) +{ + UNUSED(This); + UNUSED(dststr); + return KTX_INVALID_OPERATION; +} + +#endif + +/* + * Initialized here at the end to avoid the need for multiple declarations of + * these functions. + */ + +struct ktxTexture_vtblInt ktxTexture1_vtblInt = { + (PFNCALCDATASIZELEVELS)ktxTexture1_calcDataSizeLevels, + (PFNCALCFACELODSIZE)ktxTexture1_calcFaceLodSize, + (PFNCALCLEVELOFFSET)ktxTexture1_calcLevelOffset +}; + +struct ktxTexture_vtbl ktxTexture1_vtbl = { + (PFNKTEXDESTROY)ktxTexture1_Destroy, + (PFNKTEXGETIMAGEOFFSET)ktxTexture1_GetImageOffset, + (PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture1_GetDataSizeUncompressed, + (PFNKTEXGETIMAGESIZE)ktxTexture1_GetImageSize, + (PFNKTEXITERATELEVELS)ktxTexture1_IterateLevels, + (PFNKTEXITERATELOADLEVELFACES)ktxTexture1_IterateLoadLevelFaces, + (PFNKTEXNEEDSTRANSCODING)ktxTexture1_NeedsTranscoding, + (PFNKTEXLOADIMAGEDATA)ktxTexture1_LoadImageData, + (PFNKTEXSETIMAGEFROMMEMORY)ktxTexture1_SetImageFromMemory, + (PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture1_SetImageFromStdioStream, + (PFNKTEXWRITETOSTDIOSTREAM)ktxTexture1_WriteToStdioStream, + (PFNKTEXWRITETONAMEDFILE)ktxTexture1_WriteToNamedFile, + (PFNKTEXWRITETOMEMORY)ktxTexture1_WriteToMemory, + (PFNKTEXWRITETOSTREAM)ktxTexture1_WriteToStream, +}; + +/** @} */ + diff --git a/thirdparty/libktx/lib/texture1.h b/thirdparty/libktx/lib/texture1.h new file mode 100644 index 0000000000..95734cc5f7 --- /dev/null +++ b/thirdparty/libktx/lib/texture1.h @@ -0,0 +1,46 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab textwidth=70: */ + +/* + * Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file texture1.h + * @~English + * + * @brief Declare internal ktxTexture1 functions for sharing between + * compilation units. + * + * These functions are private and should not be used outside the library. + */ + +#ifndef _TEXTURE1_H_ +#define _TEXTURE1_H_ + +#include "texture.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CLASS ktxTexture1 +#include "texture_funcs.inl" +#undef CLASS + +KTX_error_code +ktxTexture1_constructFromStreamAndHeader(ktxTexture1* This, ktxStream* pStream, + KTX_header* pHeader, + ktxTextureCreateFlags createFlags); + +ktx_uint64_t ktxTexture1_calcDataSizeTexture(ktxTexture1* This); +ktx_size_t ktxTexture1_calcLevelOffset(ktxTexture1* This, ktx_uint32_t level); +ktx_uint32_t ktxTexture1_glTypeSize(ktxTexture1* This); + +#ifdef __cplusplus +} +#endif + +#endif /* _TEXTURE1_H_ */ diff --git a/thirdparty/libktx/lib/texture2.c b/thirdparty/libktx/lib/texture2.c new file mode 100644 index 0000000000..afbe7dcbfe --- /dev/null +++ b/thirdparty/libktx/lib/texture2.c @@ -0,0 +1,2524 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file texture2.c + * @~English + * + * @brief ktxTexture2 implementation. Support for KTX2 format. + * + * @author Mark Callow, www.edgewise-consulting.com + */ + +#if defined(_WIN32) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include <stdlib.h> +#include <string.h> +#include <zstd.h> +#include <zstd_errors.h> +#include <KHR/khr_df.h> + +#include "dfdutils/dfd.h" +#include "ktx.h" +#include "ktxint.h" +#include "filestream.h" +#include "memstream.h" +#include "texture2.h" +#include "unused.h" +#include "vk_format.h" + +// FIXME: Test this #define and put it in a header somewhere. +//#define IS_BIG_ENDIAN (1 == *(unsigned char *)&(const int){0x01000000ul}) +#define IS_BIG_ENDIAN 0 + +struct ktxTexture_vtbl ktxTexture2_vtbl; +struct ktxTexture_vtblInt ktxTexture2_vtblInt; + +#if !defined(BITFIELD_ORDER_FROM_MSB) +// Most compilers, including all those tested so far, including clang, gcc +// and msvc, order bitfields from the lsb so these struct declarations work. +// Could this be because I've only tested on little-endian machines? +// These are preferred as they are much easier to manually initialize +// and verify. +struct sampleType { + uint32_t bitOffset: 16; + uint32_t bitLength: 8; + uint32_t channelType: 8; // Includes qualifiers + uint32_t samplePosition0: 8; + uint32_t samplePosition1: 8; + uint32_t samplePosition2: 8; + uint32_t samplePosition3: 8; + uint32_t lower; + uint32_t upper; +}; + +struct BDFD { + uint32_t vendorId: 17; + uint32_t descriptorType: 15; + uint32_t versionNumber: 16; + uint32_t descriptorBlockSize: 16; + uint32_t model: 8; + uint32_t primaries: 8; + uint32_t transfer: 8; + uint32_t flags: 8; + uint32_t texelBlockDimension0: 8; + uint32_t texelBlockDimension1: 8; + uint32_t texelBlockDimension2: 8; + uint32_t texelBlockDimension3: 8; + uint32_t bytesPlane0: 8; + uint32_t bytesPlane1: 8; + uint32_t bytesPlane2: 8; + uint32_t bytesPlane3: 8; + uint32_t bytesPlane4: 8; + uint32_t bytesPlane5: 8; + uint32_t bytesPlane6: 8; + uint32_t bytesPlane7: 8; + struct sampleType samples[6]; +}; + +struct BDFD e5b9g9r9_ufloat_comparator = { + .vendorId = 0, + .descriptorType = 0, + .versionNumber = 2, + .descriptorBlockSize = sizeof(struct BDFD), + .model = KHR_DF_MODEL_RGBSDA, + .primaries = KHR_DF_PRIMARIES_BT709, + .transfer = KHR_DF_TRANSFER_LINEAR, + .flags = KHR_DF_FLAG_ALPHA_STRAIGHT, + .texelBlockDimension0 = 0, + .texelBlockDimension1 = 0, + .texelBlockDimension2 = 0, + .texelBlockDimension3 = 0, + .bytesPlane0 = 4, + .bytesPlane1 = 0, + .bytesPlane2 = 0, + .bytesPlane3 = 0, + .bytesPlane4 = 0, + .bytesPlane5 = 0, + .bytesPlane6 = 0, + .bytesPlane7 = 0, + // gcc likes this way. It does not like, e.g., + // .samples[0].bitOffset = 0, etc. which is accepted by both clang & msvc. + // I find the standards docs impenetrable so I don't know which is correct. + .samples[0] = { + .bitOffset = 0, + .bitLength = 8, + .channelType = KHR_DF_CHANNEL_RGBSDA_RED, + .samplePosition0 = 0, + .samplePosition1 = 0, + .samplePosition2 = 0, + .samplePosition3 = 0, + .lower = 0, + .upper = 8448, + }, + .samples[1] = { + .bitOffset = 27, + .bitLength = 4, + .channelType = KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT, + .samplePosition0 = 0, + .samplePosition1 = 0, + .samplePosition2 = 0, + .samplePosition3 = 0, + .lower = 15, + .upper = 31, + }, + .samples[2] = { + .bitOffset = 9, + .bitLength = 8, + .channelType = KHR_DF_CHANNEL_RGBSDA_GREEN, + .samplePosition0 = 0, + .samplePosition1 = 0, + .samplePosition2 = 0, + .samplePosition3 = 0, + .lower = 0, + .upper = 8448, + }, + .samples[3] = { + .bitOffset = 27, + .bitLength = 4, + .channelType = KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT, + .samplePosition0 = 0, + .samplePosition1 = 0, + .samplePosition2 = 0, + .samplePosition3 = 0, + .lower = 15, + .upper = 31, + }, + .samples[4] = { + .bitOffset = 18, + .bitLength = 8, + .channelType = KHR_DF_CHANNEL_RGBSDA_BLUE, + .samplePosition0 = 0, + .samplePosition1 = 0, + .samplePosition2 = 0, + .samplePosition3 = 0, + .lower = 0, + .upper = 8448, + }, + .samples[5] = { + .bitOffset = 27, + .bitLength = 4, + .channelType = KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT, + .samplePosition0 = 0, + .samplePosition1 = 0, + .samplePosition2 = 0, + .samplePosition3 = 0, + .lower = 15, + .upper = 31, + } +}; +#else +// For compilers which order bitfields from the msb rather than lsb. +#define shift(x,val) ((val) << KHR_DF_SHIFT_ ## x) +#define sampleshift(x,val) ((val) << KHR_DF_SAMPLESHIFT_ ## x) +#define e5b9g9r9_bdbwordcount KHR_DFDSIZEWORDS(6) +ktx_uint32_t e5b9g9r9_ufloat_comparator[e5b9g9r9_bdbwordcount] = { + 0, // descriptorType & vendorId + shift(DESCRIPTORBLOCKSIZE, e5b9g9r9_bdbwordcount * sizeof(ktx_uint32_t)) | shift(VERSIONNUMBER, 2), + // N.B. Allow various values of primaries, transfer & flags + shift(FLAGS, KHR_DF_FLAG_ALPHA_STRAIGHT) | shift(TRANSFER, KHR_DF_TRANSFER_LINEAR) | shift(PRIMARIES, KHR_DF_PRIMARIES_BT709) | shift(MODEL, KHR_DF_MODEL_RGBSDA), + 0, // texelBlockDimension3~0 + shift(BYTESPLANE0, 4), // All other bytesPlane fields are 0. + 0, // bytesPlane7~4 + sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 0), + 0, // samplePosition3~0 + 0, // sampleLower + 8448, // sampleUpper + sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27), + 0, // samplePosition3~0 + 15, // sampleLower + 31, // sampleUpper + sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 9), + 0, // samplePosition3~0 + 0, // sampleLower + 8448, // sampleUpper + sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27), + 0, // samplePosition3~0 + 15, // sampleLower + 31, // sampleUpper + sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 18), + 0, // samplePosition3~0 + 0, // sampleLower + 8448, // sampleUpper + sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27), + 0, // samplePosition3~0 + 15, // sampleLower + 31, // sampleUpper +}; +#endif + +/** +* @private +* @~English +* @brief Initialize a ktxFormatSize object from the info in a DFD. +* +* This is used instead of referring to the DFD directly so code dealing +* with format info can be common to KTX 1 & 2. +* +* @param[in] This pointer the ktxTexture2 whose DFD to use. +* @param[in] fi pointer to the ktxFormatSize object to initialize. +* +* @return KTX_TRUE on success, otherwise KTX_FALSE. +*/ +bool +ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd) +{ + uint32_t* pBdb = pDfd + 1; + + // Check the DFD is of the expected type and version. + if (*pBdb != 0) { + // Either decriptorType or vendorId is not 0 + return false; + } + if (KHR_DFDVAL(pBdb, VERSIONNUMBER) != KHR_DF_VERSIONNUMBER_1_3) { + return false; + } + + // DFD has supported type and version. Process it. + This->blockWidth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION0) + 1; + This->blockHeight = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION1) + 1; + This->blockDepth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION2) + 1; + This->blockSizeInBits = KHR_DFDVAL(pBdb, BYTESPLANE0) * 8; + This->paletteSizeInBits = 0; // No paletted formats in ktx v2. + This->flags = 0; + This->minBlocksX = This->minBlocksY = 1; + if (KHR_DFDVAL(pBdb, MODEL) >= KHR_DF_MODEL_DXT1A) { + // A block compressed format. Entire block is a single sample. + This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT; + if (KHR_DFDVAL(pBdb, MODEL) == KHR_DF_MODEL_PVRTC) { + This->minBlocksX = This->minBlocksY = 2; + } + } else { + // An uncompressed format. + + // Special case depth & depth stencil formats + if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_DEPTH) { + if (KHR_DFDSAMPLECOUNT(pBdb) == 1) { + This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT; + } else if (KHR_DFDSAMPLECOUNT(pBdb) == 2) { + This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT; + This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT; + This->flags |= KTX_FORMAT_SIZE_PACKED_BIT; + } else { + return false; + } + } else if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_STENCIL) { + This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT; + } else if (KHR_DFDSAMPLECOUNT(pBdb) == 6 +#if !defined(BITFIELD_ORDER_FROM_MSB) + && !memcmp(((uint32_t*)&e5b9g9r9_ufloat_comparator) + KHR_DF_WORD_TEXELBLOCKDIMENSION0, &pBdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0], sizeof(e5b9g9r9_ufloat_comparator)-(KHR_DF_WORD_TEXELBLOCKDIMENSION0)*sizeof(uint32_t))) { +#else + && !memcmp(&e5b9g9r9_ufloat_comparator[KHR_DF_WORD_TEXELBLOCKDIMENSION0], &pBdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0], sizeof(e5b9g9r9_ufloat_comparator)-(KHR_DF_WORD_TEXELBLOCKDIMENSION0)*sizeof(uint32_t))) { +#endif + // Special case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 as interpretDFD + // only handles "simple formats", i.e. where channels are described + // in contiguous bits. + This->flags |= KTX_FORMAT_SIZE_PACKED_BIT; + } else { + InterpretedDFDChannel rgba[4]; + uint32_t wordBytes; + enum InterpretDFDResult result; + + result = interpretDFD(pDfd, &rgba[0], &rgba[1], &rgba[2], &rgba[3], + &wordBytes); + if (result >= i_UNSUPPORTED_ERROR_BIT) + return false; + if (result & i_PACKED_FORMAT_BIT) + This->flags |= KTX_FORMAT_SIZE_PACKED_BIT; + } + } + if (This->blockSizeInBits == 0) { + // The DFD shows a supercompressed texture. Complete the ktxFormatSize + // struct by figuring out the post inflation value for bytesPlane0. + // Setting it here simplifies stuff later in this file. Setting the + // post inflation block size here will not cause any problems for + // the following reasons. (1) in v2 files levelIndex is always used to + // calculate data size and, of course, for the level offsets. (2) Finer + // grain access to supercompressed data than levels is not possible. + uint32_t blockByteLength; + recreateBytesPlane0FromSampleInfo(pDfd, &blockByteLength); + This->blockSizeInBits = blockByteLength * 8; + } + return true; +} + +/** + * @private + * @~English + * @brief Create a DFD for a VkFormat. + * + * This KTX-specific function adds support for combined depth stencil formats + * which are not supported by @e dfdutils' @c vk2dfd function because they + * are not seen outside a Vulkan device. KTX has its own definitions for + * these that enable uploading, with some effort. + * + * @param[in] vkFormat the format for which to create a DFD. + */ +static uint32_t* +ktxVk2dfd(ktx_uint32_t vkFormat) +{ + switch(vkFormat) { + case VK_FORMAT_D16_UNORM_S8_UINT: + // 2 16-bit words. D16 in the first. S8 in the 8 LSBs of the second. + return createDFDDepthStencil(16, 8, 4); + case VK_FORMAT_D24_UNORM_S8_UINT: + // 1 32-bit word. D24 in the MSBs. S8 in the LSBs. + return createDFDDepthStencil(24, 8, 4); + case VK_FORMAT_D32_SFLOAT_S8_UINT: + // 2 32-bit words. D32 float in the first word. S8 in LSBs of the + // second. + return createDFDDepthStencil(32, 8, 8); + default: + return vk2dfd(vkFormat); + } +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Do the part of ktxTexture2 construction that is common to + * new textures and those constructed from a stream. + * + * @param[in] This pointer to a ktxTexture2-sized block of memory to + * initialize. + * @param[in] numLevels the number of levels the texture must have. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data. + */ +static KTX_error_code +ktxTexture2_constructCommon(ktxTexture2* This, ktx_uint32_t numLevels) +{ + assert(This != NULL); + ktx_size_t privateSize; + + This->classId = ktxTexture2_c; + This->vtbl = &ktxTexture2_vtbl; + This->_protected->_vtbl = ktxTexture2_vtblInt; + privateSize = sizeof(ktxTexture2_private) + + sizeof(ktxLevelIndexEntry) * (numLevels - 1); + This->_private = (ktxTexture2_private*)malloc(privateSize); + if (This->_private == NULL) { + return KTX_OUT_OF_MEMORY; + } + memset(This->_private, 0, privateSize); + return KTX_SUCCESS; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Construct a new, empty, ktxTexture2. + * + * @param[in] This pointer to a ktxTexture2-sized block of memory to + * initialize. + * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with + * information describing the texture. + * @param[in] storageAllocation + * enum indicating whether or not to allocate storage + * for the texture images. + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture or image data. + * @exception KTX_UNSUPPORTED_TEXTURE_TYPE + * The request VkFormat is one of the + * prohibited formats. + */ +static KTX_error_code +ktxTexture2_construct(ktxTexture2* This, ktxTextureCreateInfo* createInfo, + ktxTextureCreateStorageEnum storageAllocation) +{ + ktxFormatSize formatSize; + KTX_error_code result; + + memset(This, 0, sizeof(*This)); + + if (createInfo->vkFormat != VK_FORMAT_UNDEFINED) { + This->pDfd = ktxVk2dfd(createInfo->vkFormat); + if (!This->pDfd) + return KTX_INVALID_VALUE; // Format is unknown or unsupported. + +#ifdef _DEBUG + // If this fires, an unsupported format or incorrect DFD + // has crept into vk2dfd. + assert(ktxFormatSize_initFromDfd(&formatSize, This->pDfd)); +#else + (void)ktxFormatSize_initFromDfd(&formatSize, This->pDfd); +#endif + + } else { + // TODO: Validate createInfo->pDfd. + This->pDfd = (ktx_uint32_t*)malloc(*createInfo->pDfd); + if (!This->pDfd) + return KTX_OUT_OF_MEMORY; + memcpy(This->pDfd, createInfo->pDfd, *createInfo->pDfd); + if (ktxFormatSize_initFromDfd(&formatSize, This->pDfd)) { + result = KTX_UNSUPPORTED_TEXTURE_TYPE; + goto cleanup; + } + } + + result = ktxTexture_construct(ktxTexture(This), createInfo, &formatSize); + + if (result != KTX_SUCCESS) + return result; + result = ktxTexture2_constructCommon(This, createInfo->numLevels); + if (result != KTX_SUCCESS) + goto cleanup;; + + This->vkFormat = createInfo->vkFormat; + + // Ideally we'd set all these things in ktxFormatSize_initFromDfd + // but This->_protected is not allocated until ktxTexture_construct; + if (This->isCompressed) + This->_protected->_typeSize = 1; + else if (formatSize.flags & KTX_FORMAT_SIZE_PACKED_BIT) + This->_protected->_typeSize = formatSize.blockSizeInBits / 8; + else if (formatSize.flags & (KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT)) { + if (createInfo->vkFormat == VK_FORMAT_D16_UNORM_S8_UINT) + This->_protected->_typeSize = 2; + else + This->_protected->_typeSize = 4; + } else { + // Unpacked and uncompressed + uint32_t numComponents; + getDFDComponentInfoUnpacked(This->pDfd, &numComponents, + &This->_protected->_typeSize); + } + + This->supercompressionScheme = KTX_SS_NONE; + + This->_private->_requiredLevelAlignment + = ktxTexture2_calcRequiredLevelAlignment(This); + + // Create levelIndex. Offsets are from start of the KTX2 stream. + ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex; + + This->_private->_firstLevelFileOffset = 0; + + for (ktx_uint32_t level = 0; level < This->numLevels; level++) { + levelIndex[level].uncompressedByteLength = + ktxTexture_calcLevelSize(ktxTexture(This), level, + KTX_FORMAT_VERSION_TWO); + levelIndex[level].byteLength = + levelIndex[level].uncompressedByteLength; + levelIndex[level].byteOffset = + ktxTexture_calcLevelOffset(ktxTexture(This), level); + } + + // Allocate storage, if requested. + if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) { + This->dataSize + = ktxTexture_calcDataSizeTexture(ktxTexture(This)); + This->pData = malloc(This->dataSize); + if (This->pData == NULL) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + } + return result; + +cleanup: + ktxTexture2_destruct(This); + return result; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Construct a ktxTexture by copying a source ktxTexture. + * + * @param[in] This pointer to a ktxTexture2-sized block of memory to + * initialize. + * @param[in] orig pointer to the source texture to copy. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data. + */ +static KTX_error_code +ktxTexture2_constructCopy(ktxTexture2* This, ktxTexture2* orig) +{ + KTX_error_code result; + + memcpy(This, orig, sizeof(ktxTexture2)); + // Zero all the pointers to make error handling easier + This->_protected = NULL; + This->_private = NULL; + This->pDfd = NULL; + This->kvData = NULL; + This->kvDataHead = NULL; + This->pData = NULL; + + This->_protected = + (ktxTexture_protected*)malloc(sizeof(ktxTexture_protected)); + if (!This->_protected) + return KTX_OUT_OF_MEMORY; + // Must come before memcpy of _protected so as to close an active stream. + if (!orig->pData && ktxTexture_isActiveStream((ktxTexture*)orig)) + ktxTexture2_LoadImageData(orig, NULL, 0); + memcpy(This->_protected, orig->_protected, sizeof(ktxTexture_protected)); + + ktx_size_t privateSize = sizeof(ktxTexture2_private) + + sizeof(ktxLevelIndexEntry) * (orig->numLevels - 1); + This->_private = (ktxTexture2_private*)malloc(privateSize); + if (This->_private == NULL) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + memcpy(This->_private, orig->_private, privateSize); + if (orig->_private->_sgdByteLength > 0) { + This->_private->_supercompressionGlobalData + = (ktx_uint8_t*)malloc(orig->_private->_sgdByteLength); + if (!This->_private->_supercompressionGlobalData) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + memcpy(This->_private->_supercompressionGlobalData, + orig->_private->_supercompressionGlobalData, + orig->_private->_sgdByteLength); + } + + This->pDfd = (ktx_uint32_t*)malloc(*orig->pDfd); + if (!This->pDfd) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + memcpy(This->pDfd, orig->pDfd, *orig->pDfd); + + if (orig->kvDataHead) { + ktxHashList_ConstructCopy(&This->kvDataHead, orig->kvDataHead); + } else if (orig->kvData) { + This->kvData = (ktx_uint8_t*)malloc(orig->kvDataLen); + if (!This->kvData) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + memcpy(This->kvData, orig->kvData, orig->kvDataLen); + } + + // Can't share the image data as the data pointer is exposed in the + // ktxTexture2 structure. Changing it to a ref-counted pointer would + // break code. Maybe that's okay as we're still pre-release. But, + // since this constructor will be mostly be used when transcoding + // supercompressed images, it is probably not too big a deal to make + // a copy of the data. + This->pData = (ktx_uint8_t*)malloc(This->dataSize); + if (This->pData == NULL) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + memcpy(This->pData, orig->pData, orig->dataSize); + return KTX_SUCCESS; + +cleanup: + if (This->_protected) free(This->_protected); + if (This->_private) { + if (This->_private->_supercompressionGlobalData) + free(This->_private->_supercompressionGlobalData); + free(This->_private); + } + if (This->pDfd) free (This->pDfd); + if (This->kvDataHead) ktxHashList_Destruct(&This->kvDataHead); + + return result; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Construct a ktxTexture from a ktxStream reading from a KTX source. + * + * The KTX header, which must have been read prior to calling this, is passed + * to the function. + * + * The stream object is copied into the constructed ktxTexture2. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture. + * + * If either KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT or + * KTX_TEXTURE_CREATE_RAW_KVDATA_BIT is set then the ktxTexture's orientation + * fields will be set to defaults even if the KTX source contains + * KTXorientation metadata. + * + * @param[in] This pointer to a ktxTexture2-sized block of memory to + * initialize. + * @param[in] pStream pointer to the stream to read. + * @param[in] pHeader pointer to a KTX header that has already been read from + * the stream. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_DATA_ERROR + * Source data is inconsistent with the KTX + * specification. + * @exception KTX_FILE_READ_ERROR + * An error occurred while reading the source. + * @exception KTX_FILE_UNEXPECTED_EOF + * Not enough data in the source. + * @exception KTX_OUT_OF_MEMORY Not enough memory to load either the images or + * the key-value data. + * @exception KTX_UNKNOWN_FILE_FORMAT + * The source is not in KTX format. + * @exception KTX_UNSUPPORTED_TEXTURE_TYPE + * The source describes a texture type not + * supported by OpenGL or Vulkan, e.g, a 3D array. + */ +KTX_error_code +ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream, + KTX_header2* pHeader, + ktxTextureCreateFlags createFlags) +{ + ktxTexture2_private* private; + KTX_error_code result; + KTX_supplemental_info suppInfo; + ktxStream* stream; + ktx_size_t levelIndexSize; + + assert(pHeader != NULL && pStream != NULL); + + memset(This, 0, sizeof(*This)); + result = ktxTexture_constructFromStream(ktxTexture(This), pStream, + createFlags); + if (result != KTX_SUCCESS) + return result; + + result = ktxCheckHeader2_(pHeader, &suppInfo); + if (result != KTX_SUCCESS) + goto cleanup; + // ktxCheckHeader2_ has done the max(1, levelCount) on pHeader->levelCount. + result = ktxTexture2_constructCommon(This, pHeader->levelCount); + if (result != KTX_SUCCESS) + goto cleanup; + private = This->_private; + + stream = ktxTexture2_getStream(This); + + /* + * Initialize from pHeader->info. + */ + This->vkFormat = pHeader->vkFormat; + This->supercompressionScheme = pHeader->supercompressionScheme; + + This->_protected->_typeSize = pHeader->typeSize; + // Can these be done by a ktxTexture_constructFromStream? + This->numDimensions = suppInfo.textureDimension; + This->baseWidth = pHeader->pixelWidth; + assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4); + switch (suppInfo.textureDimension) { + case 1: + This->baseHeight = This->baseDepth = 1; + break; + case 2: + This->baseHeight = pHeader->pixelHeight; + This->baseDepth = 1; + break; + case 3: + This->baseHeight = pHeader->pixelHeight; + This->baseDepth = pHeader->pixelDepth; + break; + } + if (pHeader->layerCount > 0) { + This->numLayers = pHeader->layerCount; + This->isArray = KTX_TRUE; + } else { + This->numLayers = 1; + This->isArray = KTX_FALSE; + } + This->numFaces = pHeader->faceCount; + if (pHeader->faceCount == 6) + This->isCubemap = KTX_TRUE; + else + This->isCubemap = KTX_FALSE; + // ktxCheckHeader2_ does the max(1, levelCount) and sets + // suppInfo.generateMipmaps when it was originally 0. + This->numLevels = pHeader->levelCount; + This->generateMipmaps = suppInfo.generateMipmaps; + + // Read level index + levelIndexSize = sizeof(ktxLevelIndexEntry) * This->numLevels; + result = stream->read(stream, &private->_levelIndex, levelIndexSize); + if (result != KTX_SUCCESS) + goto cleanup; + // Rebase index to start of data and save file offset. + private->_firstLevelFileOffset + = private->_levelIndex[This->numLevels-1].byteOffset; + for (ktx_uint32_t level = 0; level < This->numLevels; level++) { + private->_levelIndex[level].byteOffset + -= private->_firstLevelFileOffset; + } + + // Read DFD + This->pDfd = + (ktx_uint32_t*)malloc(pHeader->dataFormatDescriptor.byteLength); + if (!This->pDfd) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + result = stream->read(stream, This->pDfd, + pHeader->dataFormatDescriptor.byteLength); + if (result != KTX_SUCCESS) + goto cleanup; + + if (!ktxFormatSize_initFromDfd(&This->_protected->_formatSize, This->pDfd)) { + result = KTX_UNSUPPORTED_TEXTURE_TYPE; + goto cleanup; + } + This->isCompressed = (This->_protected->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT); + + if (This->supercompressionScheme == KTX_SS_BASIS_LZ + && KHR_DFDVAL(This->pDfd + 1, MODEL) != KHR_DF_MODEL_ETC1S) + { + result = KTX_FILE_DATA_ERROR; + goto cleanup; + } + + This->_private->_requiredLevelAlignment + = ktxTexture2_calcRequiredLevelAlignment(This); + + // Make an empty hash list. + ktxHashList_Construct(&This->kvDataHead); + // Load KVData. + if (pHeader->keyValueData.byteLength > 0) { + if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) { + ktx_uint32_t kvdLen = pHeader->keyValueData.byteLength; + ktx_uint8_t* pKvd; + + pKvd = malloc(kvdLen); + if (pKvd == NULL) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + + result = stream->read(stream, pKvd, kvdLen); + if (result != KTX_SUCCESS) + goto cleanup; + + if (IS_BIG_ENDIAN) { + /* Swap the counts inside the key & value data. */ + ktx_uint8_t* src = pKvd; + ktx_uint8_t* end = pKvd + kvdLen; + while (src < end) { + ktx_uint32_t keyAndValueByteSize = *((ktx_uint32_t*)src); + _ktxSwapEndian32(&keyAndValueByteSize, 1); + src += _KTX_PAD4(keyAndValueByteSize); + } + } + + if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) { + char* orientationStr; + ktx_uint32_t orientationLen; + ktx_uint32_t animData[3]; + ktx_uint32_t animDataLen; + + result = ktxHashList_Deserialize(&This->kvDataHead, + kvdLen, pKvd); + free(pKvd); + if (result != KTX_SUCCESS) { + goto cleanup; + } + + result = ktxHashList_FindValue(&This->kvDataHead, + KTX_ORIENTATION_KEY, + &orientationLen, + (void**)&orientationStr); + assert(result != KTX_INVALID_VALUE); + if (result == KTX_SUCCESS) { + // Length includes the terminating NUL. + if (orientationLen != This->numDimensions + 1) { + // There needs to be an entry for each dimension of + // the texture. + result = KTX_FILE_DATA_ERROR; + goto cleanup; + } else { + switch (This->numDimensions) { + case 3: + This->orientation.z = orientationStr[2]; + FALLTHROUGH; + case 2: + This->orientation.y = orientationStr[1]; + FALLTHROUGH; + case 1: + This->orientation.x = orientationStr[0]; + } + } + } else { + result = KTX_SUCCESS; // Not finding orientation is okay. + } + result = ktxHashList_FindValue(&This->kvDataHead, + KTX_ANIMDATA_KEY, + &animDataLen, + (void**)animData); + assert(result != KTX_INVALID_VALUE); + if (result == KTX_SUCCESS) { + if (animDataLen != sizeof(animData)) { + result = KTX_FILE_DATA_ERROR; + goto cleanup; + } + if (This->isArray) { + This->isVideo = KTX_TRUE; + This->duration = animData[0]; + This->timescale = animData[1]; + This->loopcount = animData[2]; + } else { + // animData is only valid for array textures. + result = KTX_FILE_DATA_ERROR; + goto cleanup; + } + } else { + result = KTX_SUCCESS; // Not finding video is okay. + } + } else { + This->kvDataLen = kvdLen; + This->kvData = pKvd; + } + } else { + stream->skip(stream, pHeader->keyValueData.byteLength); + } + } + + if (pHeader->supercompressionGlobalData.byteLength > 0) { + // There could be padding here so seek to the next item. + (void)stream->setpos(stream, + pHeader->supercompressionGlobalData.byteOffset); + + // Read supercompressionGlobalData + private->_supercompressionGlobalData = + (ktx_uint8_t*)malloc(pHeader->supercompressionGlobalData.byteLength); + if (!private->_supercompressionGlobalData) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + private->_sgdByteLength + = pHeader->supercompressionGlobalData.byteLength; + result = stream->read(stream, private->_supercompressionGlobalData, + private->_sgdByteLength); + + if (result != KTX_SUCCESS) + goto cleanup; + } + + // Calculate size of the image data. Level 0 is the last level in the data. + This->dataSize = private->_levelIndex[0].byteOffset + + private->_levelIndex[0].byteLength; + + /* + * Load the images, if requested. + */ + if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) { + result = ktxTexture2_LoadImageData(This, NULL, 0); + } + if (result != KTX_SUCCESS) + goto cleanup; + + return result; + +cleanup: + ktxTexture2_destruct(This); + return result; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Construct a ktxTexture from a ktxStream reading from a KTX source. + * + * The stream object is copied into the constructed ktxTexture2. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture. + * + * @param[in] This pointer to a ktxTexture2-sized block of memory to + * initialize. + * @param[in] pStream pointer to the stream to read. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_READ_ERROR + * An error occurred while reading the source. + * + * For other exceptions see ktxTexture2_constructFromStreamAndHeader(). + */ +static KTX_error_code +ktxTexture2_constructFromStream(ktxTexture2* This, ktxStream* pStream, + ktxTextureCreateFlags createFlags) +{ + KTX_header2 header; + KTX_error_code result; + + // Read header. + result = pStream->read(pStream, &header, KTX2_HEADER_SIZE); + if (result != KTX_SUCCESS) + return result; + +#if IS_BIG_ENDIAN + // byte swap the header +#endif + return ktxTexture2_constructFromStreamAndHeader(This, pStream, + &header, createFlags); +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Construct a ktxTexture from a stdio stream reading from a KTX source. + * + * See ktxTextureInt_constructFromStream for details. + * + * @note Do not close the stdio stream until you are finished with the texture + * object. + * + * @param[in] This pointer to a ktxTextureInt-sized block of memory to + * initialize. + * @param[in] stdioStream a stdio FILE pointer opened on the source. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE Either @p stdiostream or @p This is null. + * + * For other exceptions, see ktxTexture_constructFromStream(). + */ +static KTX_error_code +ktxTexture2_constructFromStdioStream(ktxTexture2* This, FILE* stdioStream, + ktxTextureCreateFlags createFlags) +{ + KTX_error_code result; + ktxStream stream; + + if (stdioStream == NULL || This == NULL) + return KTX_INVALID_VALUE; + + result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE); + if (result == KTX_SUCCESS) + result = ktxTexture2_constructFromStream(This, &stream, createFlags); + return result; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Construct a ktxTexture from a named KTX file. + * + * See ktxTextureInt_constructFromStream for details. + * + * @param[in] This pointer to a ktxTextureInt-sized block of memory to + * initialize. + * @param[in] filename pointer to a char array containing the file name. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_OPEN_FAILED The file could not be opened. + * @exception KTX_INVALID_VALUE @p filename is @c NULL. + * + * For other exceptions, see ktxTexture_constructFromStream(). + */ +static KTX_error_code +ktxTexture2_constructFromNamedFile(ktxTexture2* This, + const char* const filename, + ktxTextureCreateFlags createFlags) +{ + KTX_error_code result; + ktxStream stream; + FILE* file; + + if (This == NULL || filename == NULL) + return KTX_INVALID_VALUE; + + file = fopen(filename, "rb"); + if (!file) + return KTX_FILE_OPEN_FAILED; + + result = ktxFileStream_construct(&stream, file, KTX_TRUE); + if (result == KTX_SUCCESS) + result = ktxTexture2_constructFromStream(This, &stream, createFlags); + + return result; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Construct a ktxTexture from KTX-formatted data in memory. + * + * See ktxTextureInt_constructFromStream for details. + * + * @param[in] This pointer to a ktxTextureInt-sized block of memory to + * initialize. + * @param[in] bytes pointer to the memory containing the serialized KTX data. + * @param[in] size length of the KTX data in bytes. + * @param[in] createFlags bitmask requesting specific actions during creation. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0. + * + * For other exceptions, see ktxTexture_constructFromStream(). + */ +static KTX_error_code +ktxTexture2_constructFromMemory(ktxTexture2* This, + const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags) +{ + KTX_error_code result; + ktxStream stream; + + if (bytes == NULL || size == 0) + return KTX_INVALID_VALUE; + + result = ktxMemStream_construct_ro(&stream, bytes, size); + if (result == KTX_SUCCESS) + result = ktxTexture2_constructFromStream(This, &stream, createFlags); + + return result; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Destruct a ktxTexture2, freeing and internal memory. + * + * @param[in] This pointer to a ktxTexture2-sized block of memory to + * initialize. + */ +void +ktxTexture2_destruct(ktxTexture2* This) +{ + if (This->pDfd) free(This->pDfd); + if (This->_private) { + ktx_uint8_t* sgd = This->_private->_supercompressionGlobalData; + if (sgd) free(sgd); + free(This->_private); + } + ktxTexture_destruct(ktxTexture(This)); +} + +/** + * @memberof ktxTexture2 + * @ingroup writer + * @~English + * @brief Create a new empty ktxTexture2. + * + * The address of the newly created ktxTexture2 is written to the location + * pointed at by @p newTex. + * + * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with + * information describing the texture. + * @param[in] storageAllocation + * enum indicating whether or not to allocate storage + * for the texture images. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a + * valid OpenGL internal format value. + * @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2 + * or 3. + * @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in + * @p createInfo is 0. + * @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6. + * @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0. + * @exception KTX_INVALID_OPERATION + * The <tt>base{Width,Height,Depth}</tt> specified + * in @p createInfo are inconsistent with + * @c numDimensions. + * @exception KTX_INVALID_OPERATION + * @p createInfo is requesting a 3D array or + * 3D cubemap texture. + * @exception KTX_INVALID_OPERATION + * @p createInfo is requesting a cubemap with + * non-square or non-2D images. + * @exception KTX_INVALID_OPERATION + * @p createInfo is requesting more mip levels + * than needed for the specified + * <tt>base{Width,Height,Depth}</tt>. + * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture's images. + */ +KTX_error_code +ktxTexture2_Create(ktxTextureCreateInfo* createInfo, + ktxTextureCreateStorageEnum storageAllocation, + ktxTexture2** newTex) +{ + KTX_error_code result; + + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture2_construct(tex, createInfo, storageAllocation); + if (result != KTX_SUCCESS) { + free(tex); + } else { + *newTex = tex; + } + return result; +} + +/** + * @memberof ktxTexture2 + * @ingroup writer + * @~English + * @brief Create a ktxTexture2 by making a copy of a ktxTexture2. + * + * The address of the newly created ktxTexture2 is written to the location + * pointed at by @p newTex. + * + * @param[in] orig pointer to the texture to copy. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data. + */ + KTX_error_code + ktxTexture2_CreateCopy(ktxTexture2* orig, ktxTexture2** newTex) + { + KTX_error_code result; + + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture2_constructCopy(tex, orig); + if (result != KTX_SUCCESS) { + free(tex); + } else { + *newTex = tex; + } + return result; + + } + +/** + * @defgroup reader Reader + * @brief Read KTX-formatted data. + * @{ + */ + +/** + * @memberof ktxTexture2 + * @~English + * @brief Create a ktxTexture2 from a stdio stream reading from a KTX source. + * + * The address of a newly created ktxTexture2 reflecting the contents of the + * stdio stream is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture. + * + * @param[in] stdioStream stdio FILE pointer created from the desired file. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p newTex is @c NULL. + * @exception KTX_FILE_DATA_ERROR + * Source data is inconsistent with the KTX + * specification. + * @exception KTX_FILE_READ_ERROR + * An error occurred while reading the source. + * @exception KTX_FILE_UNEXPECTED_EOF + * Not enough data in the source. + * @exception KTX_OUT_OF_MEMORY Not enough memory to create the texture object, + * load the images or load the key-value data. + * @exception KTX_UNKNOWN_FILE_FORMAT + * The source is not in KTX format. + * @exception KTX_UNSUPPORTED_TEXTURE_TYPE + * The source describes a texture type not + * supported by OpenGL or Vulkan, e.g, a 3D array. + */ +KTX_error_code +ktxTexture2_CreateFromStdioStream(FILE* stdioStream, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex) +{ + KTX_error_code result; + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture2_constructFromStdioStream(tex, stdioStream, + createFlags); + if (result == KTX_SUCCESS) + *newTex = (ktxTexture2*)tex; + else { + free(tex); + *newTex = NULL; + } + return result; +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Create a ktxTexture2 from a named KTX file. + * + * The address of a newly created ktxTexture2 reflecting the contents of the + * file is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture. + * + * @param[in] filename pointer to a char array containing the file name. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + + * @exception KTX_FILE_OPEN_FAILED The file could not be opened. + * @exception KTX_INVALID_VALUE @p filename is @c NULL. + * + * For other exceptions, see ktxTexture_CreateFromStdioStream(). + */ +KTX_error_code +ktxTexture2_CreateFromNamedFile(const char* const filename, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex) +{ + KTX_error_code result; + + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture2_constructFromNamedFile(tex, filename, createFlags); + if (result == KTX_SUCCESS) + *newTex = (ktxTexture2*)tex; + else { + free(tex); + *newTex = NULL; + } + return result; +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Create a ktxTexture2 from KTX-formatted data in memory. + * + * The address of a newly created ktxTexture2 reflecting the contents of the + * serialized KTX data is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture. + * + * @param[in] bytes pointer to the memory containing the serialized KTX data. + * @param[in] size length of the KTX data in bytes. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0. + * + * For other exceptions, see ktxTexture_CreateFromStdioStream(). + */ +KTX_error_code +ktxTexture2_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex) +{ + KTX_error_code result; + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture2_constructFromMemory(tex, bytes, size, + createFlags); + if (result == KTX_SUCCESS) + *newTex = (ktxTexture2*)tex; + else { + free(tex); + *newTex = NULL; + } + return result; +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Create a ktxTexture2 from KTX-formatted data from a stream. + * + * The address of a newly created ktxTexture2 reflecting the contents of the + * serialized KTX data is written to the location pointed at by @p newTex. + * + * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, + * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This + * will minimize memory usage by allowing, for example, loading the images + * directly from the source into a Vulkan staging buffer. + * + * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is + * provided solely to enable implementation of the @e libktx v1 API on top of + * ktxTexture. + * + * @param[in] stream pointer to the stream to read KTX data from. + * @param[in] createFlags bitmask requesting specific actions during creation. + * @param[in,out] newTex pointer to a location in which store the address of + * the newly created texture. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0. + * + * For other exceptions, see ktxTexture_CreateFromStdioStream(). + */ +KTX_error_code +ktxTexture2_CreateFromStream(ktxStream* stream, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex) +{ + KTX_error_code result; + if (newTex == NULL) + return KTX_INVALID_VALUE; + + ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2)); + if (tex == NULL) + return KTX_OUT_OF_MEMORY; + + result = ktxTexture2_constructFromStream(tex, stream, createFlags); + if (result == KTX_SUCCESS) + *newTex = (ktxTexture2*)tex; + else { + free(tex); + *newTex = NULL; + } + return result; +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Destroy a ktxTexture2 object. + * + * This frees the memory associated with the texture contents and the memory + * of the ktxTexture2 object. This does @e not delete any OpenGL or Vulkan + * texture objects created by ktxTexture2_GLUpload or ktxTexture2_VkUpload. + * + * @param[in] This pointer to the ktxTexture2 object to destroy + */ +void +ktxTexture2_Destroy(ktxTexture2* This) +{ + ktxTexture2_destruct(This); + free(This); +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Calculate the size of the image data for the specified number + * of levels. + * + * The data size is the sum of the sizes of each level up to the number + * specified and includes any @c mipPadding between levels. It does + * not include initial @c mipPadding required in the file. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] levels number of levels whose data size to return. + * + * @return the data size in bytes. + */ +ktx_size_t +ktxTexture2_calcDataSizeLevels(ktxTexture2* This, ktx_uint32_t levels) +{ + ktx_size_t dataSize = 0; + + assert(This != NULL); + assert(This->supercompressionScheme == KTX_SS_NONE); + assert(levels <= This->numLevels); + for (ktx_uint32_t i = levels - 1; i > 0; i--) { + ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i, + KTX_FORMAT_VERSION_TWO); + dataSize += _KTX_PADN(This->_private->_requiredLevelAlignment, + levelSize); + } + dataSize += ktxTexture_calcLevelSize(ktxTexture(This), 0, + KTX_FORMAT_VERSION_TWO); + return dataSize; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * + * @copydoc ktxTexture::ktxTexture_doCalcFaceLodSize + */ +ktx_size_t +ktxTexture2_calcFaceLodSize(ktxTexture2* This, ktx_uint32_t level) +{ + assert(This != NULL); + assert(This->supercompressionScheme == KTX_SS_NONE); + /* + * For non-array cubemaps this is the size of a face. For everything + * else it is the size of the level. + */ + if (This->isCubemap && !This->isArray) + return ktxTexture_calcImageSize(ktxTexture(This), level, + KTX_FORMAT_VERSION_TWO); + else + return This->_private->_levelIndex[level].uncompressedByteLength; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Return the offset of a level in bytes from the start of the image + * data in a ktxTexture. + * + * Since the offset is from the start of the image data, it does not include the initial + * @c mipPadding required in the file. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level level whose offset to return. + * + * @return the data size in bytes. + */ +ktx_size_t +ktxTexture2_calcLevelOffset(ktxTexture2* This, ktx_uint32_t level) +{ + assert (This != NULL); + assert(This->supercompressionScheme == KTX_SS_NONE); + assert (level < This->numLevels); + ktx_size_t levelOffset = 0; + for (ktx_uint32_t i = This->numLevels - 1; i > level; i--) { + ktx_size_t levelSize; + levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i, + KTX_FORMAT_VERSION_TWO); + levelOffset += _KTX_PADN(This->_private->_requiredLevelAlignment, + levelSize); + } + return levelOffset; +} + + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Retrieve the offset of a level's first image within the KTX2 file. + * + * @param[in] This pointer to the ktxTexture object of interest. + */ +ktx_uint64_t ktxTexture2_levelFileOffset(ktxTexture2* This, ktx_uint32_t level) +{ + assert(This->_private->_firstLevelFileOffset != 0); + return This->_private->_levelIndex[level].byteOffset + + This->_private->_firstLevelFileOffset; +} + +// Recursive function to return the greatest common divisor of a and b. +static uint32_t +gcd(uint32_t a, uint32_t b) { + if (a == 0) + return b; + return gcd(b % a, a); +} + +// Function to return the least common multiple of a & 4. +uint32_t +lcm4(uint32_t a) +{ + if (!(a & 0x03)) + return a; // a is a multiple of 4. + return (a*4) / gcd(a, 4); +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Return the required alignment for levels of this texture. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * + * @return The required alignment for levels. + */ + ktx_uint32_t + ktxTexture2_calcRequiredLevelAlignment(ktxTexture2* This) + { + ktx_uint32_t alignment; + if (This->supercompressionScheme != KTX_SS_NONE) + alignment = 1; + else + alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8); + return alignment; + } + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Return what the required alignment for levels of this texture will be after inflation. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * + * @return The required alignment for levels. + */ +ktx_uint32_t +ktxTexture2_calcPostInflationLevelAlignment(ktxTexture2* This) +{ + ktx_uint32_t alignment; + + // Should actually work for none supercompressed but don't want to + // encourage use of it. + assert(This->supercompressionScheme >= KTX_SS_ZSTD); + + if (This->vkFormat != VK_FORMAT_UNDEFINED) + alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8); + else + alignment = 16; + + return alignment; +} + + +/** + * @memberof ktxTexture2 + * @~English + * @brief Return information about the components of an image in a texture. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in,out] pNumComponents pointer to location in which to write the + * number of components in the textures images. + * @param[in,out] pComponentByteLength + * pointer to the location in which to write + * byte length of a component. + */ +void +ktxTexture2_GetComponentInfo(ktxTexture2* This, uint32_t* pNumComponents, + uint32_t* pComponentByteLength) +{ + // FIXME Need to handle packed case. + getDFDComponentInfoUnpacked(This->pDfd, pNumComponents, + pComponentByteLength); +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Return the number of components in an image of the texture. + * + * Returns the number of components indicated by the DFD's sample information + * in accordance with the color model. For uncompressed formats it will be the actual + * number of components in the image. For block-compressed formats, it will be 1 or 2 + * according to the format's DFD color model. For Basis compressed textures, the + * function examines the ids of the channels indicated by the DFD and uses that + * information to determine and return the number of components in the image + * @e before encoding and deflation so it can be used to help choose a suitable + * transcode target format. + * + * @param[in] This pointer to the ktxTexture object of interest. + * + * @return the number of components. + */ +ktx_uint32_t +ktxTexture2_GetNumComponents(ktxTexture2* This) +{ + uint32_t* pBdb = This->pDfd + 1; + uint32_t dfdNumComponents = getDFDNumComponents(This->pDfd); + uint32_t colorModel = KHR_DFDVAL(pBdb, MODEL); + if (colorModel < KHR_DF_MODEL_DXT1A) { + return dfdNumComponents; + } else { + switch (colorModel) { + case KHR_DF_MODEL_ETC1S: + { + uint32_t channel0Id = KHR_DFDSVAL(pBdb, 0, CHANNELID); + if (dfdNumComponents == 1) { + if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB) + return 3; + else + return 1; + } else { + uint32_t channel1Id = KHR_DFDSVAL(pBdb, 1, CHANNELID); + if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB + && channel1Id == KHR_DF_CHANNEL_ETC1S_AAA) + return 4; + else { + // An invalid combination of channel Ids should never + // have been set during creation or should have been + // caught when the file was loaded. + assert(channel0Id == KHR_DF_CHANNEL_ETC1S_RRR + && channel1Id == KHR_DF_CHANNEL_ETC1S_GGG); + return 2; + } + } + break; + } + case KHR_DF_MODEL_UASTC: + switch (KHR_DFDSVAL(pBdb, 0, CHANNELID)) { + case KHR_DF_CHANNEL_UASTC_RRR: + return 1; + case KHR_DF_CHANNEL_UASTC_RRRG: + return 2; + case KHR_DF_CHANNEL_UASTC_RGB: + return 3; + case KHR_DF_CHANNEL_UASTC_RGBA: + return 4; + default: + // Same comment as for the assert in the ETC1 case. + assert(false); + return 1; + } + break; + default: + return dfdNumComponents; + } + } +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Find the offset of an image within a ktxTexture's image data. + * + * As there is no such thing as a 3D cubemap we make the 3rd location parameter + * do double duty. Only works for non-supercompressed textures as + * there is no way to tell where an image is for a supercompressed one. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] level mip level of the image. + * @param[in] layer array layer of the image. + * @param[in] faceSlice cube map face or depth slice of the image. + * @param[in,out] pOffset pointer to location to store the offset. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_OPERATION + * @p level, @p layer or @p faceSlice exceed the + * dimensions of the texture. + * @exception KTX_INVALID_OPERATION Texture is supercompressed. + * @exception KTX_INVALID_VALID @p This is NULL. + */ +KTX_error_code +ktxTexture2_GetImageOffset(ktxTexture2* This, ktx_uint32_t level, + ktx_uint32_t layer, ktx_uint32_t faceSlice, + ktx_size_t* pOffset) +{ + if (This == NULL) + return KTX_INVALID_VALUE; + + if (level >= This->numLevels || layer >= This->numLayers) + return KTX_INVALID_OPERATION; + + if (This->supercompressionScheme != KTX_SS_NONE) + return KTX_INVALID_OPERATION; + + if (This->isCubemap) { + if (faceSlice >= This->numFaces) + return KTX_INVALID_OPERATION; + } else { + ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level); + if (faceSlice >= maxSlice) + return KTX_INVALID_OPERATION; + } + + // Get the offset of the start of the level. + *pOffset = ktxTexture2_levelDataOffset(This, level); + + // All layers, faces & slices within a level are the same size. + if (layer != 0) { + ktx_size_t layerSize; + layerSize = ktxTexture_layerSize(ktxTexture(This), level, + KTX_FORMAT_VERSION_TWO); + *pOffset += layer * layerSize; + } + if (faceSlice != 0) { + ktx_size_t imageSize; + imageSize = ktxTexture2_GetImageSize(This, level); + *pOffset += faceSlice * imageSize; + } + return KTX_SUCCESS; +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Retrieve the opto-electrical transfer function of the images. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * + * @return A @c khr_df_transfer enum value specifying the OETF. + */ +khr_df_transfer_e +ktxTexture2_GetOETF_e(ktxTexture2* This) +{ + return KHR_DFDVAL(This->pDfd+1, TRANSFER); +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Retrieve the opto-electrical transfer function of the images. + * @deprecated Retained for backward compatibility. Use ktxTexture2\_GetOETF\_e() + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * + * @return A @c khr_df_transfer enum value specifying the OETF, returned as + * @c ktx_uint32_t. + */ +ktx_uint32_t +ktxTexture2_GetOETF(ktxTexture2* This) +{ + return KHR_DFDVAL(This->pDfd+1, TRANSFER); +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Retrieve the DFD color model of the images. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * + * @return A @c khr_df_transfer enum value specifying the color model. + */ +khr_df_model_e +ktxTexture2_GetColorModel_e(ktxTexture2* This) +{ + return KHR_DFDVAL(This->pDfd+1, MODEL); +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Retrieve whether the RGB components have been premultiplied by the alpha component. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * + * @return KTX\_TRUE if the components are premultiplied, KTX_FALSE otherwise. + */ +ktx_bool_t +ktxTexture2_GetPremultipliedAlpha(ktxTexture2* This) +{ + return KHR_DFDVAL(This->pDfd+1, FLAGS) & KHR_DF_FLAG_ALPHA_PREMULTIPLIED; +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Query if the images are in a transcodable format. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + */ +ktx_bool_t +ktxTexture2_NeedsTranscoding(ktxTexture2* This) +{ + if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_ETC1S) + return true; + else if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_UASTC) + return true; + else + return false; +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Return the total size in bytes of the uncompressed data of a ktxTexture2. + * + * If supercompressionScheme == KTX_SS_NONE or + * KTX_SS_BASIS_LZ, returns the value of @c This->dataSize + * else if supercompressionScheme == KTX_SS_ZSTD, it returns the + * sum of the uncompressed sizes of each mip level plus space for the level padding. With no + * supercompression the data size and uncompressed data size are the same. For Basis + * supercompression the uncompressed size cannot be known until the data is transcoded + * so the compressed size is returned. + * + * @param[in] This pointer to the ktxTexture1 object of interest. + */ +ktx_size_t +ktxTexture2_GetDataSizeUncompressed(ktxTexture2* This) +{ + switch (This->supercompressionScheme) { + case KTX_SS_BASIS_LZ: + case KTX_SS_NONE: + return This->dataSize; + case KTX_SS_ZSTD: + { + ktx_size_t uncompressedSize = 0; + ktx_uint32_t uncompressedLevelAlignment; + ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex; + + uncompressedLevelAlignment = + ktxTexture2_calcPostInflationLevelAlignment(This); + + for (ktx_int32_t level = This->numLevels - 1; level >= 1; level--) { + ktx_size_t uncompressedLevelSize; + uncompressedLevelSize = levelIndex[level].uncompressedByteLength; + uncompressedLevelSize = _KTX_PADN(uncompressedLevelAlignment, + uncompressedLevelSize); + uncompressedSize += uncompressedLevelSize; + } + uncompressedSize += levelIndex[0].uncompressedByteLength; + return uncompressedSize; + } + case KTX_SS_BEGIN_VENDOR_RANGE: + case KTX_SS_END_VENDOR_RANGE: + case KTX_SS_BEGIN_RESERVED: + default: + return 0; + } +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Calculate & return the size in bytes of an image at the specified + * mip level. + * + * For arrays, this is the size of a layer, for cubemaps, the size of a face + * and for 3D textures, the size of a depth slice. + * + * The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * @param[in] level level of interest. * + */ +ktx_size_t +ktxTexture2_GetImageSize(ktxTexture2* This, ktx_uint32_t level) +{ + return ktxTexture_calcImageSize(ktxTexture(This), level, + KTX_FORMAT_VERSION_TWO); +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Iterate over the mip levels in a ktxTexture2 object. + * + * This is almost identical to ktxTexture_IterateLevelFaces(). The difference is + * that the blocks of image data for non-array cube maps include all faces of + * a mip level. + * + * This function works even if @p This->pData == 0 so it can be used to + * obtain offsets and sizes for each level by callers who have loaded the data + * externally. + * + * Intended for use only when supercompressionScheme == SUPERCOMPRESSION_NONE. + * + * @param[in] This handle of the ktxTexture opened on the data. + * @param[in,out] iterCb the address of a callback function which is called + * with the data for each image block. + * @param[in,out] userdata the address of application-specific data which is + * passed to the callback along with the image data. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. The + * following are returned directly by this function. @p iterCb may + * return these for other causes or may return additional errors. + * + * @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not + * decreasing + * @exception KTX_INVALID_OPERATION supercompressionScheme != SUPERCOMPRESSION_NONE. + * @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL. + * + */ +KTX_error_code +ktxTexture2_IterateLevels(ktxTexture2* This, PFNKTXITERCB iterCb, void* userdata) +{ + KTX_error_code result = KTX_SUCCESS; + //ZSTD_DCtx* dctx; + //ktx_uint8_t* decompBuf; + ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex; + + if (This == NULL) + return KTX_INVALID_VALUE; + + if (iterCb == NULL) + return KTX_INVALID_VALUE; + + if (This->supercompressionScheme != KTX_SS_NONE) + return KTX_INVALID_OPERATION; + + for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--) + { + ktx_uint32_t width, height, depth; + ktx_uint64_t levelSize; + ktx_uint64_t offset; + + /* Array textures have the same number of layers at each mip level. */ + width = MAX(1, This->baseWidth >> level); + height = MAX(1, This->baseHeight >> level); + depth = MAX(1, This->baseDepth >> level); + + levelSize = levelIndex[level].uncompressedByteLength; + offset = ktxTexture2_levelDataOffset(This, level); + + /* All array layers are passed in a group because that is how + * GL & Vulkan need them. Hence no + * for (layer = 0; layer < This->numLayers) + */ + result = iterCb(level, 0, width, height, depth, + levelSize, This->pData + offset, userdata); + if (result != KTX_SUCCESS) + break; + } + + return result; +} + +/** + * @memberof ktxTexture2 + * @~English + * @brief Iterate over the images in a ktxTexture2 object while loading the + * image data. + * + * This operates similarly to ktxTexture_IterateLevelFaces() except that it + * loads the images from the ktxTexture2's source to a temporary buffer + * while iterating. If supercompressionScheme == KTX_SS_ZSTD, + * it will inflate the data before passing it to the callback. The callback function + * must copy the image data if it wishes to preserve it as the temporary buffer + * is reused for each level and is freed when this function exits. + * + * This function is helpful for reducing memory usage when uploading the data + * to a graphics API. + * + * Intended for use only when supercompressionScheme == SUPERCOMPRESSION_NONE + * or SUPERCOMPRESSION_ZSTD. As there is no access to the ktxTexture's data on + * conclusion of this function, destroying the texture on completion is recommended. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * @param[in,out] iterCb the address of a callback function which is called + * with the data for each image. + * @param[in,out] userdata the address of application-specific data which is + * passed to the callback along with the image data. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. The + * following are returned directly by this function. @p iterCb may + * return these for other causes or may return additional errors. + * + * @exception KTX_FILE_DATA_ERROR mip level sizes are increasing not + * decreasing + * @exception KTX_INVALID_OPERATION the ktxTexture2 was not created from a + * stream, i.e there is no data to load, or + * this ktxTexture2's images have already + * been loaded. + * @exception KTX_INVALID_OPERATION + * supercompressionScheme != SUPERCOMPRESSION_NONE. + * and supercompressionScheme != SUPERCOMPRESSION_ZSTD. + * @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL. + * @exception KTX_OUT_OF_MEMORY not enough memory to allocate a block to + * hold the base level image. + */ +KTX_error_code +ktxTexture2_IterateLoadLevelFaces(ktxTexture2* This, PFNKTXITERCB iterCb, + void* userdata) +{ + DECLARE_PROTECTED(ktxTexture); + ktxStream* stream = (ktxStream *)&prtctd->_stream; + ktxLevelIndexEntry* levelIndex; + ktx_size_t dataSize = 0, uncompressedDataSize = 0; + KTX_error_code result = KTX_SUCCESS; + ktx_uint8_t* dataBuf = NULL; + ktx_uint8_t* uncompressedDataBuf = NULL; + ktx_uint8_t* pData; + ZSTD_DCtx* dctx = NULL; + + if (This == NULL) + return KTX_INVALID_VALUE; + + if (This->classId != ktxTexture2_c) + return KTX_INVALID_OPERATION; + + if (This->supercompressionScheme != KTX_SS_NONE && + This->supercompressionScheme != KTX_SS_ZSTD) + return KTX_INVALID_OPERATION; + + if (iterCb == NULL) + return KTX_INVALID_VALUE; + + if (prtctd->_stream.data.file == NULL) + // This Texture not created from a stream or images are already loaded. + return KTX_INVALID_OPERATION; + + levelIndex = This->_private->_levelIndex; + + // Allocate memory sufficient for the base level + dataSize = levelIndex[0].byteLength; + dataBuf = malloc(dataSize); + if (!dataBuf) + return KTX_OUT_OF_MEMORY; + if (This->supercompressionScheme == KTX_SS_ZSTD) { + uncompressedDataSize = levelIndex[0].uncompressedByteLength; + uncompressedDataBuf = malloc(uncompressedDataSize); + if (!uncompressedDataBuf) { + result = KTX_OUT_OF_MEMORY; + goto cleanup; + } + dctx = ZSTD_createDCtx(); + pData = uncompressedDataBuf; + } else { + pData = dataBuf; + } + + for (ktx_int32_t level = This->numLevels - 1; level >= 0; --level) + { + ktx_size_t levelSize; + GLsizei width, height, depth; + + // Array textures have the same number of layers at each mip level. + width = MAX(1, This->baseWidth >> level); + height = MAX(1, This->baseHeight >> level); + depth = MAX(1, This->baseDepth >> level); + + levelSize = levelIndex[level].byteLength; + if (dataSize < levelSize) { + // Levels cannot be larger than the base level + result = KTX_FILE_DATA_ERROR; + goto cleanup; + } + + // Use setpos so we skip any padding. + result = stream->setpos(stream, + ktxTexture2_levelFileOffset(This, level)); + if (result != KTX_SUCCESS) + goto cleanup; + + result = stream->read(stream, dataBuf, levelSize); + if (result != KTX_SUCCESS) + goto cleanup; + + if (This->supercompressionScheme == KTX_SS_ZSTD) { + levelSize = + ZSTD_decompressDCtx(dctx, uncompressedDataBuf, + uncompressedDataSize, + dataBuf, + levelSize); + if (ZSTD_isError(levelSize)) { + ZSTD_ErrorCode error = ZSTD_getErrorCode(levelSize); + switch(error) { + case ZSTD_error_dstSize_tooSmall: + return KTX_INVALID_VALUE; // inflatedDataCapacity too small. + case ZSTD_error_memory_allocation: + return KTX_OUT_OF_MEMORY; + default: + return KTX_FILE_DATA_ERROR; + } + } + // We don't fix up the texture's dataSize, levelIndex or + // _requiredAlignment because after this function completes there + // is no way to get at the texture's data. + //nindex[level].byteOffset = levelOffset; + //nindex[level].uncompressedByteLength = nindex[level].byteLength = + //levelByteLength; + } + +#if IS_BIG_ENDIAN + switch (prtctd->_typeSize) { + case 2: + _ktxSwapEndian16((ktx_uint16_t*)pData, levelSize / 2); + break; + case 4: + _ktxSwapEndian32((ktx_uint32_t*)pDest, levelSize / 4); + break; + case 8: + _ktxSwapEndian64((ktx_uint64_t*)pDest, levelSize / 8); + break; + } +#endif + + // With the exception of non-array cubemaps the entire level + // is passed at once because that is how OpenGL and Vulkan need them. + // Vulkan could take all the faces at once too but we iterate + // them separately for OpenGL. + if (This->isCubemap && !This->isArray) { + ktx_uint8_t* pFace = pData; + struct blockCount { + ktx_uint32_t x, y; + } blockCount; + ktx_size_t faceSize; + + blockCount.x + = (uint32_t)ceilf((float)width / prtctd->_formatSize.blockWidth); + blockCount.y + = (uint32_t)ceilf((float)height / prtctd->_formatSize.blockHeight); + blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x); + blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y); + faceSize = blockCount.x * blockCount.y + * prtctd->_formatSize.blockSizeInBits / 8; + + for (ktx_uint32_t face = 0; face < This->numFaces; ++face) { + result = iterCb(level, face, + width, height, depth, + (ktx_uint32_t)faceSize, pFace, userdata); + pFace += faceSize; + if (result != KTX_SUCCESS) + goto cleanup; + } + } else { + result = iterCb(level, 0, + width, height, depth, + (ktx_uint32_t)levelSize, pData, userdata); + if (result != KTX_SUCCESS) + goto cleanup; + } + } + + // No further need for this. + stream->destruct(stream); + This->_private->_firstLevelFileOffset = 0; +cleanup: + free(dataBuf); + if (uncompressedDataBuf) free(uncompressedDataBuf); + if (dctx) ZSTD_freeDCtx(dctx); + + return result; +} + +KTX_error_code +ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData, + ktx_uint8_t* pInflatedData, + ktx_size_t inflatedDataCapacity); +/** + * @memberof ktxTexture2 + * @~English + * @brief Load all the image data from the ktxTexture2's source. + * + * The data will be inflated if supercompressionScheme == SUPERCOMPRESSION_ZSTD. + * The data is loaded into the provided buffer or to an internally allocated + * buffer, if @p pBuffer is @c NULL. Callers providing their own buffer must + * ensure the buffer large enough to hold the inflated data for files deflated + * with Zstd. See ktxTexture2_GetDataSizeUncompressed(). + * + * The texture's levelIndex, dataSize, DFD and supercompressionScheme will + * all be updated after successful inflation to reflect the inflated data. + * + * @param[in] This pointer to the ktxTexture object of interest. + * @param[in] pBuffer pointer to the buffer in which to load the image data. + * @param[in] bufSize size of the buffer pointed at by @p pBuffer. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p This is NULL. + * @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size. + * @exception KTX_INVALID_OPERATION + * The data has already been loaded or the + * ktxTexture was not created from a KTX source. + * @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data. + */ +KTX_error_code +ktxTexture2_LoadImageData(ktxTexture2* This, + ktx_uint8_t* pBuffer, ktx_size_t bufSize) +{ + DECLARE_PROTECTED(ktxTexture); + DECLARE_PRIVATE(ktxTexture2); + ktx_uint8_t* pDest; + ktx_uint8_t* pDeflatedData = 0; + ktx_uint8_t* pReadBuf; + KTX_error_code result = KTX_SUCCESS; + ktx_size_t inflatedDataCapacity = ktxTexture2_GetDataSizeUncompressed(This); + + if (This == NULL) + return KTX_INVALID_VALUE; + + if (This->pData != NULL) + return KTX_INVALID_OPERATION; // Data already loaded. + + if (prtctd->_stream.data.file == NULL) + // This Texture not created from a stream or images already loaded; + return KTX_INVALID_OPERATION; + + if (pBuffer == NULL) { + This->pData = malloc(inflatedDataCapacity); + if (This->pData == NULL) + return KTX_OUT_OF_MEMORY; + pDest = This->pData; + } else if (bufSize < inflatedDataCapacity) { + return KTX_INVALID_VALUE; + } else { + pDest = pBuffer; + } + + if (This->supercompressionScheme == KTX_SS_ZSTD) { + // Create buffer to hold deflated data. + pDeflatedData = malloc(This->dataSize); + if (pDeflatedData == NULL) + return KTX_OUT_OF_MEMORY; + pReadBuf = pDeflatedData; + } else { + pReadBuf = pDest; + } + + // Seek to data for first level as there may be padding between the + // metadata/sgd and the image data. + + result = prtctd->_stream.setpos(&prtctd->_stream, + private->_firstLevelFileOffset); + if (result != KTX_SUCCESS) + return result; + + result = prtctd->_stream.read(&prtctd->_stream, pReadBuf, + This->dataSize); + if (result != KTX_SUCCESS) + return result; + + if (This->supercompressionScheme == KTX_SS_ZSTD) { + assert(pDeflatedData != NULL); + result = ktxTexture2_inflateZstdInt(This, pDeflatedData, pDest, + inflatedDataCapacity); + free(pDeflatedData); + if (result != KTX_SUCCESS) { + if (pBuffer == NULL) { + free(This->pData); + This->pData = 0; + } + return result; + } + } + + if (IS_BIG_ENDIAN) { + // Perform endianness conversion on texture data. + // To avoid mip padding, need to convert each level individually. + for (ktx_uint32_t level = 0; level < This->numLevels; ++level) + { + ktx_size_t levelOffset; + ktx_size_t levelByteLength; + + levelByteLength = private->_levelIndex[level].byteLength; + levelOffset = ktxTexture2_levelDataOffset(This, level); + pDest = This->pData + levelOffset; + switch (prtctd->_typeSize) { + case 2: + _ktxSwapEndian16((ktx_uint16_t*)pDest, levelByteLength / 2); + break; + case 4: + _ktxSwapEndian32((ktx_uint32_t*)pDest, levelByteLength / 4); + break; + case 8: + _ktxSwapEndian64((ktx_uint64_t*)pDest, levelByteLength / 8); + break; + } + } + } + + // No further need for stream or file offset. + prtctd->_stream.destruct(&prtctd->_stream); + private->_firstLevelFileOffset = 0; + return result; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Retrieve the offset of a level's first image within the ktxTexture2's + * image data. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + */ +ktx_uint64_t ktxTexture2_levelDataOffset(ktxTexture2* This, ktx_uint32_t level) +{ + return This->_private->_levelIndex[level].byteOffset; +} + +/** + * @memberof ktxTexture2 @private + * @~English + * @brief Inflate the data in a ktxTexture2 object using Zstandard. + * + * The texture's levelIndex, dataSize, DFD and supercompressionScheme will + * all be updated after successful inflation to reflect the inflated data. + * + * @param[in] This pointer to the ktxTexture2 object of interest. + * @param[in] pDeflatedData pointer to a buffer containing the deflated data + * of the entire texture. + * @param[in,out] pInflatedData pointer to a buffer in which to write the inflated + * data. + * @param[in] inflatedDataCapacity capacity of the buffer pointed at by + * @p pInflatedData. + */ +KTX_error_code +ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData, + ktx_uint8_t* pInflatedData, + ktx_size_t inflatedDataCapacity) +{ + DECLARE_PROTECTED(ktxTexture); + ktx_uint32_t levelIndexByteLength = + This->numLevels * sizeof(ktxLevelIndexEntry); + uint64_t levelOffset = 0; + ktxLevelIndexEntry* cindex = This->_private->_levelIndex; + ktxLevelIndexEntry* nindex; + ktx_uint32_t uncompressedLevelAlignment; + + ZSTD_DCtx* dctx; + + if (pDeflatedData == NULL) + return KTX_INVALID_VALUE; + + if (pInflatedData == NULL) + return KTX_INVALID_VALUE; + + if (This->supercompressionScheme != KTX_SS_ZSTD) + return KTX_INVALID_OPERATION; + + nindex = malloc(levelIndexByteLength); + if (nindex == NULL) + return KTX_OUT_OF_MEMORY; + + uncompressedLevelAlignment = + ktxTexture2_calcPostInflationLevelAlignment(This); + + ktx_size_t inflatedByteLength = 0; + dctx = ZSTD_createDCtx(); + for (int32_t level = This->numLevels - 1; level >= 0; level--) { + size_t levelByteLength = + ZSTD_decompressDCtx(dctx, pInflatedData + levelOffset, + inflatedDataCapacity, + &pDeflatedData[cindex[level].byteOffset], + cindex[level].byteLength); + if (ZSTD_isError(levelByteLength)) { + ZSTD_ErrorCode error = ZSTD_getErrorCode(levelByteLength); + switch(error) { + case ZSTD_error_dstSize_tooSmall: + return KTX_INVALID_VALUE; // inflatedDataCapacity too small. + case ZSTD_error_memory_allocation: + return KTX_OUT_OF_MEMORY; + default: + return KTX_FILE_DATA_ERROR; + } + } + nindex[level].byteOffset = levelOffset; + nindex[level].uncompressedByteLength = nindex[level].byteLength = + levelByteLength; + ktx_size_t paddedLevelByteLength + = _KTX_PADN(uncompressedLevelAlignment, levelByteLength); + inflatedByteLength += paddedLevelByteLength; + levelOffset += paddedLevelByteLength; + inflatedDataCapacity -= paddedLevelByteLength; + } + ZSTD_freeDCtx(dctx); + + // Now modify the texture. + + This->dataSize = inflatedByteLength; + This->supercompressionScheme = KTX_SS_NONE; + memcpy(cindex, nindex, levelIndexByteLength); // Update level index + free(nindex); + This->_private->_requiredLevelAlignment = uncompressedLevelAlignment; + // Set bytesPlane as we're now sized. + uint32_t* bdb = This->pDfd + 1; + // blockSizeInBits was set to the inflated size on file load. + bdb[KHR_DF_WORD_BYTESPLANE0] = prtctd->_formatSize.blockSizeInBits / 8; + + return KTX_SUCCESS; +} + +#if !KTX_FEATURE_WRITE + +/* + * Stubs for writer functions that return a proper error code + */ + +KTX_error_code +ktxTexture2_SetImageFromMemory(ktxTexture2* This, ktx_uint32_t level, + ktx_uint32_t layer, ktx_uint32_t faceSlice, + const ktx_uint8_t* src, ktx_size_t srcSize) +{ + UNUSED(This); + UNUSED(level); + UNUSED(layer); + UNUSED(faceSlice); + UNUSED(src); + UNUSED(srcSize); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture2_SetImageFromStdioStream(ktxTexture2* This, ktx_uint32_t level, + ktx_uint32_t layer, ktx_uint32_t faceSlice, + FILE* src, ktx_size_t srcSize) +{ + UNUSED(This); + UNUSED(level); + UNUSED(layer); + UNUSED(faceSlice); + UNUSED(src); + UNUSED(srcSize); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture2_WriteToStdioStream(ktxTexture2* This, FILE* dstsstr) +{ + UNUSED(This); + UNUSED(dstsstr); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture2_WriteToNamedFile(ktxTexture2* This, const char* const dstname) +{ + UNUSED(This); + UNUSED(dstname); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture2_WriteToMemory(ktxTexture2* This, + ktx_uint8_t** ppDstBytes, ktx_size_t* pSize) +{ + UNUSED(This); + UNUSED(ppDstBytes); + UNUSED(pSize); + return KTX_INVALID_OPERATION; +} + +KTX_error_code +ktxTexture2_WriteToStream(ktxTexture2* This, + ktxStream* dststr) +{ + UNUSED(This); + UNUSED(dststr); + return KTX_INVALID_OPERATION; +} + +#endif + +/* + * Initialized here at the end to avoid the need for multiple declarations of + * the virtual functions. + */ + +struct ktxTexture_vtblInt ktxTexture2_vtblInt = { + (PFNCALCDATASIZELEVELS)ktxTexture2_calcDataSizeLevels, + (PFNCALCFACELODSIZE)ktxTexture2_calcFaceLodSize, + (PFNCALCLEVELOFFSET)ktxTexture2_calcLevelOffset +}; + +struct ktxTexture_vtbl ktxTexture2_vtbl = { + (PFNKTEXDESTROY)ktxTexture2_Destroy, + (PFNKTEXGETIMAGEOFFSET)ktxTexture2_GetImageOffset, + (PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture2_GetDataSizeUncompressed, + (PFNKTEXGETIMAGESIZE)ktxTexture2_GetImageSize, + (PFNKTEXITERATELEVELS)ktxTexture2_IterateLevels, + (PFNKTEXITERATELOADLEVELFACES)ktxTexture2_IterateLoadLevelFaces, + (PFNKTEXNEEDSTRANSCODING)ktxTexture2_NeedsTranscoding, + (PFNKTEXLOADIMAGEDATA)ktxTexture2_LoadImageData, + (PFNKTEXSETIMAGEFROMMEMORY)ktxTexture2_SetImageFromMemory, + (PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture2_SetImageFromStdioStream, + (PFNKTEXWRITETOSTDIOSTREAM)ktxTexture2_WriteToStdioStream, + (PFNKTEXWRITETONAMEDFILE)ktxTexture2_WriteToNamedFile, + (PFNKTEXWRITETOMEMORY)ktxTexture2_WriteToMemory, + (PFNKTEXWRITETOSTREAM)ktxTexture2_WriteToStream, +}; + +/** @} */ + diff --git a/thirdparty/libktx/lib/texture2.h b/thirdparty/libktx/lib/texture2.h new file mode 100644 index 0000000000..14e4115ecf --- /dev/null +++ b/thirdparty/libktx/lib/texture2.h @@ -0,0 +1,68 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab textwidth=70: */ + +/* + * Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file texture2.h + * @~English + * + * @brief Declare internal ktxTexture2 functions for sharing between + * compilation units. + * + * These functions are private and should not be used outside the library. + */ + +#ifndef _TEXTURE2_H_ +#define _TEXTURE2_H_ + +#include "texture.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CLASS ktxTexture2 +#include "texture_funcs.inl" +#undef CLASS + +typedef struct ktxTexture2_private { + ktx_uint8_t* _supercompressionGlobalData; + ktx_uint32_t _requiredLevelAlignment; + ktx_uint64_t _sgdByteLength; + ktx_uint64_t _firstLevelFileOffset; /*!< Always 0, unless the texture was + created from a stream and the image + data is not yet loaded. */ + // Must be last so it can grow. + ktxLevelIndexEntry _levelIndex[1]; /*!< Offsets in this index are from the + start of the image data. Use + ktxTexture_levelStreamOffset() and + ktxTexture_levelDataOffset(). The former + will add the above file offset to the + index offset. */ +} ktxTexture2_private; + +KTX_error_code +ktxTexture2_LoadImageData(ktxTexture2* This, + ktx_uint8_t* pBuffer, ktx_size_t bufSize); + +KTX_error_code +ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream, + KTX_header2* pHeader, + ktxTextureCreateFlags createFlags); + +ktx_uint64_t ktxTexture2_calcDataSizeTexture(ktxTexture2* This); +ktx_size_t ktxTexture2_calcLevelOffset(ktxTexture2* This, ktx_uint32_t level); +ktx_uint32_t ktxTexture2_calcRequiredLevelAlignment(ktxTexture2* This); +ktx_uint64_t ktxTexture2_levelFileOffset(ktxTexture2* This, ktx_uint32_t level); +ktx_uint64_t ktxTexture2_levelDataOffset(ktxTexture2* This, ktx_uint32_t level); + +#ifdef __cplusplus +} +#endif + +#endif /* _TEXTURE2_H_ */ diff --git a/thirdparty/libktx/lib/texture_funcs.inl b/thirdparty/libktx/lib/texture_funcs.inl new file mode 100644 index 0000000000..5de16a396b --- /dev/null +++ b/thirdparty/libktx/lib/texture_funcs.inl @@ -0,0 +1,76 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab textwidth=70: */ + +/* + * Copyright 2019-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file texture_funcs.h + * @~English + * + * @brief Templates for functions common to base & derived ktxTexture classes. + * + * Define CLASS before including this file. + */ + +#define CAT(c, n) PRIMITIVE_CAT(c, n) +#define PRIMITIVE_CAT(c, n) c ## _ ## n + +#define CLASS_FUNC(name) CAT(CLASS, name) + +/* + ====================================== + Virtual ktxTexture functions + ====================================== +*/ + + +void CLASS_FUNC(Destroy)(CLASS* This); +KTX_error_code CLASS_FUNC(GetImageOffset)(CLASS* This, ktx_uint32_t level, + ktx_uint32_t layer, + ktx_uint32_t faceSlice, + ktx_size_t* pOffset); +ktx_size_t CLASS_FUNC(GetImageSize)(CLASS* This, ktx_uint32_t level); +KTX_error_code CLASS_FUNC(GLUpload)(CLASS* This, GLuint* pTexture, + GLenum* pTarget, GLenum* pGlerror); +KTX_error_code CLASS_FUNC(IterateLevels)(CLASS* This, + PFNKTXITERCB iterCb, + void* userdata); +KTX_error_code CLASS_FUNC(IterateLevelFaces)(CLASS* This, + PFNKTXITERCB iterCb, + void* userdata); +KTX_error_code CLASS_FUNC(IterateLoadLevelFaces)(CLASS* This, + PFNKTXITERCB iterCb, + void* userdata); +KTX_error_code CLASS_FUNC(LoadImageData)(CLASS* This, + ktx_uint8_t* pBuffer, + ktx_size_t bufSize); +KTX_error_code CLASS_FUNC(SetImageFromStdioStream)(CLASS* This, + ktx_uint32_t level,ktx_uint32_t layer, + ktx_uint32_t faceSlice, + FILE* src, ktx_size_t srcSize); +KTX_error_code CLASS_FUNC(SetImageFromMemory)(CLASS* This, + ktx_uint32_t level, ktx_uint32_t layer, + ktx_uint32_t faceSlice, + const ktx_uint8_t* src, ktx_size_t srcSize); + +KTX_error_code CLASS_FUNC(WriteToStdioStream)(CLASS* This, FILE* dstsstr); +KTX_error_code CLASS_FUNC(WriteToNamedFile)(CLASS* This, + const char* const dstname); +KTX_error_code CLASS_FUNC(WriteToMemory)(CLASS* This, + ktx_uint8_t** ppDstBytes, ktx_size_t* pSize); +KTX_error_code CLASS_FUNC(WriteToStream)(CLASS* This, + ktxStream* dststr); + +/* + ====================================== + Internal ktxTexture functions + ====================================== +*/ + + +void CLASS_FUNC(destruct)(CLASS* This); + diff --git a/thirdparty/libktx/lib/uthash.h b/thirdparty/libktx/lib/uthash.h new file mode 100644 index 0000000000..aa4729027c --- /dev/null +++ b/thirdparty/libktx/lib/uthash.h @@ -0,0 +1,942 @@ +/* +Copyright (c) 2003-2010, Troy D. Hanson http://uthash.sourceforge.net All rights reserved. +SPDX-License-Identifier: BSD-1-Clause +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#include <string.h> /* memcmp,strlen */ +#include <stddef.h> /* ptrdiff_t */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && __cplusplus /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on win32 */ +#ifdef _MSC_VER +typedef unsigned int uint32_t; +#else +#include <inttypes.h> /* uint32_t */ +#endif + +#define UTHASH_VERSION 1.9.1 + +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#define uthash_free(ptr) free(ptr) /* free fcn */ + +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_bkt,_hf_hashv; \ + out=NULL; \ + if (head) { \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0); + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv); \ +} while (0); + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)keyptr; \ + (add)->hh.keylen = keylen_in; \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + unsigned _hd_bkt; \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl); \ + head = NULL; \ + } else { \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((char*)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield,strlen(add->strfield),add) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + unsigned _bkt_i; \ + unsigned _count, _bkt_count; \ + char *_prev; \ + struct UT_hash_handle *_thh; \ + if (head) { \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %d, actual %d\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include <unistd.h> to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6 */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)key; \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)key; \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) + +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)key; \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) \ + hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ + bkt = hashv & (num_bkts-1); \ +} while(0); + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)key; \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + char *_hj_key=(char*)key; \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = keylen; \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + char *_sfh_key=(char*)key; \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0); + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * So MurmurHash comes in two versions, the faster unaligned one and the slower + * aligned one. We only use the faster one on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__)) +#define HASH_MUR HASH_MUR_UNALIGNED +#else +#define HASH_MUR HASH_MUR_ALIGNED +#endif + +/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */ +#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const unsigned int _mur_m = 0x5bd1e995; \ + const int _mur_r = 24; \ + hashv = 0xcafebabe ^ keylen; \ + char *_mur_key = (char *)key; \ + uint32_t _mur_tmp, _mur_len = keylen; \ + \ + for (;_mur_len >= 4; _mur_len-=4) { \ + _mur_tmp = *(uint32_t *)_mur_key; \ + _mur_tmp *= _mur_m; \ + _mur_tmp ^= _mur_tmp >> _mur_r; \ + _mur_tmp *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_tmp; \ + _mur_key += 4; \ + } \ + \ + switch(_mur_len) \ + { \ + case 3: hashv ^= _mur_key[2] << 16; \ + case 2: hashv ^= _mur_key[1] << 8; \ + case 1: hashv ^= _mur_key[0]; \ + hashv *= _mur_m; \ + }; \ + \ + hashv ^= hashv >> 13; \ + hashv *= _mur_m; \ + hashv ^= hashv >> 15; \ + \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */ +#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const unsigned int _mur_m = 0x5bd1e995; \ + const int _mur_r = 24; \ + hashv = 0xcafebabe ^ keylen; \ + char *_mur_key = (char *)key; \ + uint32_t _mur_len = keylen; \ + int _mur_align = (int)_mur_key & 3; \ + \ + if (_mur_align && (_mur_len >= 4)) { \ + unsigned _mur_t = 0, _mur_d = 0; \ + switch(_mur_align) { \ + case 1: _mur_t |= _mur_key[2] << 16; \ + case 2: _mur_t |= _mur_key[1] << 8; \ + case 3: _mur_t |= _mur_key[0]; \ + } \ + _mur_t <<= (8 * _mur_align); \ + _mur_key += 4-_mur_align; \ + _mur_len -= 4-_mur_align; \ + int _mur_sl = 8 * (4-_mur_align); \ + int _mur_sr = 8 * _mur_align; \ + \ + for (;_mur_len >= 4; _mur_len-=4) { \ + _mur_d = *(unsigned *)_mur_key; \ + _mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ + unsigned _mur_k = _mur_t; \ + _mur_k *= _mur_m; \ + _mur_k ^= _mur_k >> _mur_r; \ + _mur_k *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_k; \ + _mur_t = _mur_d; \ + _mur_key += 4; \ + } \ + _mur_d = 0; \ + if(_mur_len >= _mur_align) { \ + switch(_mur_align) { \ + case 3: _mur_d |= _mur_key[2] << 16; \ + case 2: _mur_d |= _mur_key[1] << 8; \ + case 1: _mur_d |= _mur_key[0]; \ + } \ + unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ + _mur_k *= _mur_m; \ + _mur_k ^= _mur_k >> _mur_r; \ + _mur_k *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_k; \ + _mur_k += _mur_align; \ + _mur_len -= _mur_align; \ + \ + switch(_mur_len) \ + { \ + case 3: hashv ^= _mur_key[2] << 16; \ + case 2: hashv ^= _mur_key[1] << 8; \ + case 1: hashv ^= _mur_key[0]; \ + hashv *= _mur_m; \ + } \ + } else { \ + switch(_mur_len) \ + { \ + case 3: _mur_d ^= _mur_key[2] << 16; \ + case 2: _mur_d ^= _mur_key[1] << 8; \ + case 1: _mur_d ^= _mur_key[0]; \ + case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ + hashv *= _mur_m; \ + } \ + } \ + \ + hashv ^= hashv >> 13; \ + hashv *= _mur_m; \ + hashv ^= hashv >> 15; \ + } else { \ + for (;_mur_len >= 4; _mur_len-=4) { \ + unsigned _mur_k = *(unsigned*)_mur_key; \ + _mur_k *= _mur_m; \ + _mur_k ^= _mur_k >> _mur_r; \ + _mur_k *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_k; \ + _mur_key += 4; \ + } \ + switch(_mur_len) \ + { \ + case 3: hashv ^= _mur_key[2] << 16; \ + case 2: hashv ^= _mur_key[1] << 8; \ + case 1: hashv ^= _mur_key[0]; \ + hashv *= _mur_m; \ + } \ + \ + hashv ^= hashv >> 13; \ + hashv *= _mur_m; \ + hashv ^= hashv >> 15; \ + } \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if (out->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if (out->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,out->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + uthash_free( tbl->buckets ); \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + _hs_tail->next = NULL; \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets ); \ + uthash_free((head)->hh.tbl); \ + (head)=NULL; \ + } \ +} while(0) + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/thirdparty/libktx/lib/vk_format.h b/thirdparty/libktx/lib/vk_format.h new file mode 100644 index 0000000000..18adf33b59 --- /dev/null +++ b/thirdparty/libktx/lib/vk_format.h @@ -0,0 +1,1388 @@ +/* +================================================================================================ + +Description : Vulkan format properties and conversion from OpenGL. +Author : J.M.P. van Waveren +Date : 07/17/2016 +Language : C99 +Format : Real tabs with the tab size equal to 4 spaces. +Copyright : Copyright (c) 2016 Oculus VR, LLC. All Rights reserved. + + +LICENSE +======= + +Copyright 2016 Oculus VR, LLC. +SPDX-License-Identifier: Apache-2.0 + + +DESCRIPTION +=========== + +This header implements several support routines to convert OpenGL formats/types +to Vulkan formats. These routines are particularly useful for loading file +formats that store OpenGL formats/types such as KTX and glTF. + +The functions in this header file convert the format, internalFormat and type +that are used as parameters to the following OpenGL functions: + +void glTexImage2D( GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, const GLvoid * data ); +void glTexImage3D( GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLenum format, GLenum type, const GLvoid * data ); +void glCompressedTexImage2D( GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid * data ); +void glCompressedTexImage3D( GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLsizei imageSize, const GLvoid * data ); +void glTexStorage2D( GLenum target, GLsizei levels, GLenum internalformat, + GLsizei width, GLsizei height ); +void glTexStorage3D( GLenum target, GLsizei levels, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth ); +void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid * pointer); + + +IMPLEMENTATION +============== + +This file does not include OpenGL / OpenGL ES headers because: + + 1. Including OpenGL / OpenGL ES headers is platform dependent and + may require a separate installation of an OpenGL SDK. + 2. The OpenGL format/type constants are the same between extensions and core. + 3. The OpenGL format/type constants are the same between OpenGL and OpenGL ES. + 4. File formats like KTX and glTF may use OpenGL formats and types that + are not supported by the OpenGL implementation on the platform but are + supported by the Vulkan implementation. + + +ENTRY POINTS +============ + +static inline VkFormat vkGetFormatFromOpenGLFormat( const GLenum format, const GLenum type ); +static inline VkFormat vkGetFormatFromOpenGLType( const GLenum type, const GLuint numComponents, const GLboolean normalized ); +static inline VkFormat vkGetFormatFromOpenGLInternalFormat( const GLenum internalFormat ); +static inline void vkGetFormatSize( const VkFormat format, VkFormatSize * pFormatSize ); + +MODIFICATIONS for use in libktx +=============================== + +2019.5.30 Use common ktxFormatSize to return results. Mark Callow, Edgewise Consulting. +2019.6.12 Add mapping of PVRTC formats. " + +================================================================================================ +*/ + +#if !defined( VK_FORMAT_H ) +#define VK_FORMAT_H + +#include "gl_format.h" + +static inline VkFormat vkGetFormatFromOpenGLFormat( const GLenum format, const GLenum type ) +{ + switch ( type ) + { + // + // 8 bits per component + // + case GL_UNSIGNED_BYTE: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R8_UNORM; + case GL_RG: return VK_FORMAT_R8G8_UNORM; + case GL_RGB: return VK_FORMAT_R8G8B8_UNORM; + case GL_BGR: return VK_FORMAT_B8G8R8_UNORM; + case GL_RGBA: return VK_FORMAT_R8G8B8A8_UNORM; + case GL_BGRA: return VK_FORMAT_B8G8R8A8_UNORM; + case GL_RED_INTEGER: return VK_FORMAT_R8_UINT; + case GL_RG_INTEGER: return VK_FORMAT_R8G8_UINT; + case GL_RGB_INTEGER: return VK_FORMAT_R8G8B8_UINT; + case GL_BGR_INTEGER: return VK_FORMAT_B8G8R8_UINT; + case GL_RGBA_INTEGER: return VK_FORMAT_R8G8B8A8_UINT; + case GL_BGRA_INTEGER: return VK_FORMAT_B8G8R8A8_UINT; + case GL_STENCIL_INDEX: return VK_FORMAT_S8_UINT; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_BYTE: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R8_SNORM; + case GL_RG: return VK_FORMAT_R8G8_SNORM; + case GL_RGB: return VK_FORMAT_R8G8B8_SNORM; + case GL_BGR: return VK_FORMAT_B8G8R8_SNORM; + case GL_RGBA: return VK_FORMAT_R8G8B8A8_SNORM; + case GL_BGRA: return VK_FORMAT_B8G8R8A8_SNORM; + case GL_RED_INTEGER: return VK_FORMAT_R8_SINT; + case GL_RG_INTEGER: return VK_FORMAT_R8G8_SINT; + case GL_RGB_INTEGER: return VK_FORMAT_R8G8B8_SINT; + case GL_BGR_INTEGER: return VK_FORMAT_B8G8R8_SINT; + case GL_RGBA_INTEGER: return VK_FORMAT_R8G8B8A8_SINT; + case GL_BGRA_INTEGER: return VK_FORMAT_B8G8R8A8_SINT; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + + // + // 16 bits per component + // + case GL_UNSIGNED_SHORT: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R16_UNORM; + case GL_RG: return VK_FORMAT_R16G16_UNORM; + case GL_RGB: return VK_FORMAT_R16G16B16_UNORM; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R16G16B16A16_UNORM; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R16_UINT; + case GL_RG_INTEGER: return VK_FORMAT_R16G16_UINT; + case GL_RGB_INTEGER: return VK_FORMAT_R16G16B16_UINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R16G16B16A16_UINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_D16_UNORM; + case GL_DEPTH_STENCIL: return VK_FORMAT_D16_UNORM_S8_UINT; + } + break; + } + case GL_SHORT: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R16_SNORM; + case GL_RG: return VK_FORMAT_R16G16_SNORM; + case GL_RGB: return VK_FORMAT_R16G16B16_SNORM; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R16G16B16A16_SNORM; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R16_SINT; + case GL_RG_INTEGER: return VK_FORMAT_R16G16_SINT; + case GL_RGB_INTEGER: return VK_FORMAT_R16G16B16_SINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R16G16B16A16_SINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_HALF_FLOAT: + case GL_HALF_FLOAT_OES: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R16_SFLOAT; + case GL_RG: return VK_FORMAT_R16G16_SFLOAT; + case GL_RGB: return VK_FORMAT_R16G16B16_SFLOAT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R16G16B16A16_SFLOAT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RG_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGB_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + + // + // 32 bits per component + // + case GL_UNSIGNED_INT: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R32_UINT; + case GL_RG: return VK_FORMAT_R32G32_UINT; + case GL_RGB: return VK_FORMAT_R32G32B32_UINT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R32G32B32A32_UINT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R32_UINT; + case GL_RG_INTEGER: return VK_FORMAT_R32G32_UINT; + case GL_RGB_INTEGER: return VK_FORMAT_R32G32B32_UINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R32G32B32A32_UINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_X8_D24_UNORM_PACK32; + case GL_DEPTH_STENCIL: return VK_FORMAT_D24_UNORM_S8_UINT; + } + break; + } + case GL_INT: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R32_SINT; + case GL_RG: return VK_FORMAT_R32G32_SINT; + case GL_RGB: return VK_FORMAT_R32G32B32_SINT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R32G32B32A32_SINT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R32_SINT; + case GL_RG_INTEGER: return VK_FORMAT_R32G32_SINT; + case GL_RGB_INTEGER: return VK_FORMAT_R32G32B32_SINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R32G32B32A32_SINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_FLOAT: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R32_SFLOAT; + case GL_RG: return VK_FORMAT_R32G32_SFLOAT; + case GL_RGB: return VK_FORMAT_R32G32B32_SFLOAT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R32G32B32A32_SFLOAT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RG_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGB_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_D32_SFLOAT; + case GL_DEPTH_STENCIL: return VK_FORMAT_D32_SFLOAT_S8_UINT; + } + break; + } + + // + // 64 bits per component + // + case GL_UNSIGNED_INT64: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R64_UINT; + case GL_RG: return VK_FORMAT_R64G64_UINT; + case GL_RGB: return VK_FORMAT_R64G64B64_UINT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R64G64B64A64_UINT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RG_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGB_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_INT64: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R64_SINT; + case GL_RG: return VK_FORMAT_R64G64_SINT; + case GL_RGB: return VK_FORMAT_R64G64B64_SINT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R64G64B64A64_SINT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R64_SINT; + case GL_RG_INTEGER: return VK_FORMAT_R64G64_SINT; + case GL_RGB_INTEGER: return VK_FORMAT_R64G64B64_SINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R64G64B64A64_SINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_DOUBLE: + { + switch ( format ) + { + case GL_RED: return VK_FORMAT_R64_SFLOAT; + case GL_RG: return VK_FORMAT_R64G64_SFLOAT; + case GL_RGB: return VK_FORMAT_R64G64B64_SFLOAT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R64G64B64A64_SFLOAT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R64_SFLOAT; + case GL_RG_INTEGER: return VK_FORMAT_R64G64_SFLOAT; + case GL_RGB_INTEGER: return VK_FORMAT_R64G64B64_SFLOAT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R64G64B64A64_SFLOAT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + + // + // Packed + // + case GL_UNSIGNED_BYTE_3_3_2: + assert( format == GL_RGB || format == GL_RGB_INTEGER ); + return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_BYTE_2_3_3_REV: + assert( format == GL_BGR || format == GL_BGR_INTEGER ); + return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_SHORT_5_6_5: + assert( format == GL_RGB || format == GL_RGB_INTEGER ); + return VK_FORMAT_R5G6B5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_6_5_REV: + assert( format == GL_BGR || format == GL_BGR_INTEGER ); + return VK_FORMAT_B5G6R5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return VK_FORMAT_R4G4B4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return VK_FORMAT_B4G4R4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_5_5_1: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return VK_FORMAT_R5G5B5A1_UNORM_PACK16; + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return VK_FORMAT_A1R5G5B5_UNORM_PACK16; + case GL_UNSIGNED_INT_8_8_8_8: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return ( format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ) ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_UNORM; + case GL_UNSIGNED_INT_8_8_8_8_REV: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return ( format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ) ? VK_FORMAT_A8B8G8R8_UINT_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32; + case GL_UNSIGNED_INT_10_10_10_2: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return ( format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ) ? VK_FORMAT_A2R10G10B10_UINT_PACK32 : VK_FORMAT_A2R10G10B10_UNORM_PACK32; + case GL_UNSIGNED_INT_2_10_10_10_REV: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return ( format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ) ? VK_FORMAT_A2B10G10R10_UINT_PACK32 : VK_FORMAT_A2B10G10R10_UNORM_PACK32; + case GL_UNSIGNED_INT_10F_11F_11F_REV: + assert( format == GL_RGB || format == GL_BGR ); + return VK_FORMAT_B10G11R11_UFLOAT_PACK32; + case GL_UNSIGNED_INT_5_9_9_9_REV: + assert( format == GL_RGB || format == GL_BGR ); + return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; + case GL_UNSIGNED_INT_24_8: + assert( format == GL_DEPTH_STENCIL ); + return VK_FORMAT_D24_UNORM_S8_UINT; + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + assert( format == GL_DEPTH_STENCIL ); + return VK_FORMAT_D32_SFLOAT_S8_UINT; + } + + return VK_FORMAT_UNDEFINED; +} + +static inline VkFormat vkGetFormatFromOpenGLType( const GLenum type, const GLuint numComponents, const GLboolean normalized ) +{ + switch ( type ) + { + // + // 8 bits per component + // + case GL_UNSIGNED_BYTE: + { + switch ( numComponents ) + { + case 1: return normalized ? VK_FORMAT_R8_UNORM : VK_FORMAT_R8_UINT; + case 2: return normalized ? VK_FORMAT_R8G8_UNORM : VK_FORMAT_R8G8_UINT; + case 3: return normalized ? VK_FORMAT_R8G8B8_UNORM : VK_FORMAT_R8G8B8_UINT; + case 4: return normalized ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R8G8B8A8_UINT; + } + break; + } + case GL_BYTE: + { + switch ( numComponents ) + { + case 1: return normalized ? VK_FORMAT_R8_SNORM : VK_FORMAT_R8_SINT; + case 2: return normalized ? VK_FORMAT_R8G8_SNORM : VK_FORMAT_R8G8_SINT; + case 3: return normalized ? VK_FORMAT_R8G8B8_SNORM : VK_FORMAT_R8G8B8_SINT; + case 4: return normalized ? VK_FORMAT_R8G8B8A8_SNORM : VK_FORMAT_R8G8B8A8_SINT; + } + break; + } + + // + // 16 bits per component + // + case GL_UNSIGNED_SHORT: + { + switch ( numComponents ) + { + case 1: return normalized ? VK_FORMAT_R16_UNORM : VK_FORMAT_R16_UINT; + case 2: return normalized ? VK_FORMAT_R16G16_UNORM : VK_FORMAT_R16G16_UINT; + case 3: return normalized ? VK_FORMAT_R16G16B16_UNORM : VK_FORMAT_R16G16B16_UINT; + case 4: return normalized ? VK_FORMAT_R16G16B16A16_UNORM : VK_FORMAT_R16G16B16A16_UINT; + } + break; + } + case GL_SHORT: + { + switch ( numComponents ) + { + case 1: return normalized ? VK_FORMAT_R16_SNORM : VK_FORMAT_R16_SINT; + case 2: return normalized ? VK_FORMAT_R16G16_SNORM : VK_FORMAT_R16G16_SINT; + case 3: return normalized ? VK_FORMAT_R16G16B16_SNORM : VK_FORMAT_R16G16B16_SINT; + case 4: return normalized ? VK_FORMAT_R16G16B16A16_SNORM : VK_FORMAT_R16G16B16A16_SINT; + } + break; + } + case GL_HALF_FLOAT: + case GL_HALF_FLOAT_OES: + { + switch ( numComponents ) + { + case 1: return VK_FORMAT_R16_SFLOAT; + case 2: return VK_FORMAT_R16G16_SFLOAT; + case 3: return VK_FORMAT_R16G16B16_SFLOAT; + case 4: return VK_FORMAT_R16G16B16A16_SFLOAT; + } + break; + } + + // + // 32 bits per component + // + case GL_UNSIGNED_INT: + { + switch ( numComponents ) + { + case 1: return VK_FORMAT_R32_UINT; + case 2: return VK_FORMAT_R32G32_UINT; + case 3: return VK_FORMAT_R32G32B32_UINT; + case 4: return VK_FORMAT_R32G32B32A32_UINT; + } + break; + } + case GL_INT: + { + switch ( numComponents ) + { + case 1: return VK_FORMAT_R32_SINT; + case 2: return VK_FORMAT_R32G32_SINT; + case 3: return VK_FORMAT_R32G32B32_SINT; + case 4: return VK_FORMAT_R32G32B32A32_SINT; + } + break; + } + case GL_FLOAT: + { + switch ( numComponents ) + { + case 1: return VK_FORMAT_R32_SFLOAT; + case 2: return VK_FORMAT_R32G32_SFLOAT; + case 3: return VK_FORMAT_R32G32B32_SFLOAT; + case 4: return VK_FORMAT_R32G32B32A32_SFLOAT; + } + break; + } + + // + // 64 bits per component + // + case GL_UNSIGNED_INT64: + { + switch ( numComponents ) + { + case 1: return VK_FORMAT_R64_UINT; + case 2: return VK_FORMAT_R64G64_UINT; + case 3: return VK_FORMAT_R64G64B64_UINT; + case 4: return VK_FORMAT_R64G64B64A64_UINT; + } + break; + } + case GL_INT64: + { + switch ( numComponents ) + { + case 1: return VK_FORMAT_R64_SINT; + case 2: return VK_FORMAT_R64G64_SINT; + case 3: return VK_FORMAT_R64G64B64_SINT; + case 4: return VK_FORMAT_R64G64B64A64_SINT; + } + break; + } + case GL_DOUBLE: + { + switch ( numComponents ) + { + case 1: return VK_FORMAT_R64_SFLOAT; + case 2: return VK_FORMAT_R64G64_SFLOAT; + case 3: return VK_FORMAT_R64G64B64_SFLOAT; + case 4: return VK_FORMAT_R64G64B64A64_SFLOAT; + } + break; + } + + // + // Packed + // + case GL_UNSIGNED_BYTE_3_3_2: return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_BYTE_2_3_3_REV: return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_SHORT_5_6_5: return VK_FORMAT_R5G6B5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_6_5_REV: return VK_FORMAT_B5G6R5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4: return VK_FORMAT_R4G4B4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4_REV: return VK_FORMAT_B4G4R4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_5_5_1: return VK_FORMAT_R5G5B5A1_UNORM_PACK16; + case GL_UNSIGNED_SHORT_1_5_5_5_REV: return VK_FORMAT_A1R5G5B5_UNORM_PACK16; + case GL_UNSIGNED_INT_8_8_8_8: return normalized ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R8G8B8A8_UINT; + case GL_UNSIGNED_INT_8_8_8_8_REV: return normalized ? VK_FORMAT_A8B8G8R8_UNORM_PACK32 : VK_FORMAT_A8B8G8R8_UINT_PACK32; + case GL_UNSIGNED_INT_10_10_10_2: return normalized ? VK_FORMAT_A2R10G10B10_UNORM_PACK32 : VK_FORMAT_A2R10G10B10_UINT_PACK32; + case GL_UNSIGNED_INT_2_10_10_10_REV: return normalized ? VK_FORMAT_A2B10G10R10_UNORM_PACK32 : VK_FORMAT_A2B10G10R10_UINT_PACK32; + case GL_UNSIGNED_INT_10F_11F_11F_REV: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; + case GL_UNSIGNED_INT_5_9_9_9_REV: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; + case GL_UNSIGNED_INT_24_8: return VK_FORMAT_D24_UNORM_S8_UINT; + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: return VK_FORMAT_D32_SFLOAT_S8_UINT; + } + + return VK_FORMAT_UNDEFINED; +} + +static inline VkFormat vkGetFormatFromOpenGLInternalFormat( const GLenum internalFormat ) +{ + switch ( internalFormat ) + { + // + // 8 bits per component + // + case GL_R8: return VK_FORMAT_R8_UNORM; // 1-component, 8-bit unsigned normalized + case GL_RG8: return VK_FORMAT_R8G8_UNORM; // 2-component, 8-bit unsigned normalized + case GL_RGB8: return VK_FORMAT_R8G8B8_UNORM; // 3-component, 8-bit unsigned normalized + case GL_RGBA8: return VK_FORMAT_R8G8B8A8_UNORM; // 4-component, 8-bit unsigned normalized + + case GL_R8_SNORM: return VK_FORMAT_R8_SNORM; // 1-component, 8-bit signed normalized + case GL_RG8_SNORM: return VK_FORMAT_R8G8_SNORM; // 2-component, 8-bit signed normalized + case GL_RGB8_SNORM: return VK_FORMAT_R8G8B8_SNORM; // 3-component, 8-bit signed normalized + case GL_RGBA8_SNORM: return VK_FORMAT_R8G8B8A8_SNORM; // 4-component, 8-bit signed normalized + + case GL_R8UI: return VK_FORMAT_R8_UINT; // 1-component, 8-bit unsigned integer + case GL_RG8UI: return VK_FORMAT_R8G8_UINT; // 2-component, 8-bit unsigned integer + case GL_RGB8UI: return VK_FORMAT_R8G8B8_UINT; // 3-component, 8-bit unsigned integer + case GL_RGBA8UI: return VK_FORMAT_R8G8B8A8_UINT; // 4-component, 8-bit unsigned integer + + case GL_R8I: return VK_FORMAT_R8_SINT; // 1-component, 8-bit signed integer + case GL_RG8I: return VK_FORMAT_R8G8_SINT; // 2-component, 8-bit signed integer + case GL_RGB8I: return VK_FORMAT_R8G8B8_SINT; // 3-component, 8-bit signed integer + case GL_RGBA8I: return VK_FORMAT_R8G8B8A8_SINT; // 4-component, 8-bit signed integer + + case GL_SR8: return VK_FORMAT_R8_SRGB; // 1-component, 8-bit sRGB + case GL_SRG8: return VK_FORMAT_R8G8_SRGB; // 2-component, 8-bit sRGB + case GL_SRGB8: return VK_FORMAT_R8G8B8_SRGB; // 3-component, 8-bit sRGB + case GL_SRGB8_ALPHA8: return VK_FORMAT_R8G8B8A8_SRGB; // 4-component, 8-bit sRGB + + // + // 16 bits per component + // + case GL_R16: return VK_FORMAT_R16_UNORM; // 1-component, 16-bit unsigned normalized + case GL_RG16: return VK_FORMAT_R16G16_UNORM; // 2-component, 16-bit unsigned normalized + case GL_RGB16: return VK_FORMAT_R16G16B16_UNORM; // 3-component, 16-bit unsigned normalized + case GL_RGBA16: return VK_FORMAT_R16G16B16A16_UNORM; // 4-component, 16-bit unsigned normalized + + case GL_R16_SNORM: return VK_FORMAT_R16_SNORM; // 1-component, 16-bit signed normalized + case GL_RG16_SNORM: return VK_FORMAT_R16G16_SNORM; // 2-component, 16-bit signed normalized + case GL_RGB16_SNORM: return VK_FORMAT_R16G16B16_SNORM; // 3-component, 16-bit signed normalized + case GL_RGBA16_SNORM: return VK_FORMAT_R16G16B16A16_SNORM; // 4-component, 16-bit signed normalized + + case GL_R16UI: return VK_FORMAT_R16_UINT; // 1-component, 16-bit unsigned integer + case GL_RG16UI: return VK_FORMAT_R16G16_UINT; // 2-component, 16-bit unsigned integer + case GL_RGB16UI: return VK_FORMAT_R16G16B16_UINT; // 3-component, 16-bit unsigned integer + case GL_RGBA16UI: return VK_FORMAT_R16G16B16A16_UINT; // 4-component, 16-bit unsigned integer + + case GL_R16I: return VK_FORMAT_R16_SINT; // 1-component, 16-bit signed integer + case GL_RG16I: return VK_FORMAT_R16G16_SINT; // 2-component, 16-bit signed integer + case GL_RGB16I: return VK_FORMAT_R16G16B16_SINT; // 3-component, 16-bit signed integer + case GL_RGBA16I: return VK_FORMAT_R16G16B16A16_SINT; // 4-component, 16-bit signed integer + + case GL_R16F: return VK_FORMAT_R16_SFLOAT; // 1-component, 16-bit floating-point + case GL_RG16F: return VK_FORMAT_R16G16_SFLOAT; // 2-component, 16-bit floating-point + case GL_RGB16F: return VK_FORMAT_R16G16B16_SFLOAT; // 3-component, 16-bit floating-point + case GL_RGBA16F: return VK_FORMAT_R16G16B16A16_SFLOAT; // 4-component, 16-bit floating-point + + // + // 32 bits per component + // + case GL_R32UI: return VK_FORMAT_R32_UINT; // 1-component, 32-bit unsigned integer + case GL_RG32UI: return VK_FORMAT_R32G32_UINT; // 2-component, 32-bit unsigned integer + case GL_RGB32UI: return VK_FORMAT_R32G32B32_UINT; // 3-component, 32-bit unsigned integer + case GL_RGBA32UI: return VK_FORMAT_R32G32B32A32_UINT; // 4-component, 32-bit unsigned integer + + case GL_R32I: return VK_FORMAT_R32_SINT; // 1-component, 32-bit signed integer + case GL_RG32I: return VK_FORMAT_R32G32_SINT; // 2-component, 32-bit signed integer + case GL_RGB32I: return VK_FORMAT_R32G32B32_SINT; // 3-component, 32-bit signed integer + case GL_RGBA32I: return VK_FORMAT_R32G32B32A32_SINT; // 4-component, 32-bit signed integer + + case GL_R32F: return VK_FORMAT_R32_SFLOAT; // 1-component, 32-bit floating-point + case GL_RG32F: return VK_FORMAT_R32G32_SFLOAT; // 2-component, 32-bit floating-point + case GL_RGB32F: return VK_FORMAT_R32G32B32_SFLOAT; // 3-component, 32-bit floating-point + case GL_RGBA32F: return VK_FORMAT_R32G32B32A32_SFLOAT; // 4-component, 32-bit floating-point + + // + // Packed + // + case GL_R3_G3_B2: return VK_FORMAT_UNDEFINED; // 3-component 3:3:2, unsigned normalized + case GL_RGB4: return VK_FORMAT_UNDEFINED; // 3-component 4:4:4, unsigned normalized + case GL_RGB5: return VK_FORMAT_R5G5B5A1_UNORM_PACK16; // 3-component 5:5:5, unsigned normalized + case GL_RGB565: return VK_FORMAT_R5G6B5_UNORM_PACK16; // 3-component 5:6:5, unsigned normalized + case GL_RGB10: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; // 3-component 10:10:10, unsigned normalized + case GL_RGB12: return VK_FORMAT_UNDEFINED; // 3-component 12:12:12, unsigned normalized + case GL_RGBA2: return VK_FORMAT_UNDEFINED; // 4-component 2:2:2:2, unsigned normalized + case GL_RGBA4: return VK_FORMAT_R4G4B4A4_UNORM_PACK16; // 4-component 4:4:4:4, unsigned normalized + case GL_RGBA12: return VK_FORMAT_UNDEFINED; // 4-component 12:12:12:12, unsigned normalized + case GL_RGB5_A1: return VK_FORMAT_A1R5G5B5_UNORM_PACK16; // 4-component 5:5:5:1, unsigned normalized + case GL_RGB10_A2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; // 4-component 10:10:10:2, unsigned normalized + case GL_RGB10_A2UI: return VK_FORMAT_A2R10G10B10_UINT_PACK32; // 4-component 10:10:10:2, unsigned integer + case GL_R11F_G11F_B10F: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; // 3-component 11:11:10, floating-point + case GL_RGB9_E5: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; // 3-component/exp 9:9:9/5, floating-point + + // + // S3TC/DXT/BC + // + + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return VK_FORMAT_BC1_RGB_UNORM_BLOCK; // line through 3D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return VK_FORMAT_BC2_UNORM_BLOCK; // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return VK_FORMAT_BC3_UNORM_BLOCK; // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: return VK_FORMAT_BC1_RGB_SRGB_BLOCK; // line through 3D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return VK_FORMAT_BC1_RGBA_SRGB_BLOCK; // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return VK_FORMAT_BC2_SRGB_BLOCK; // line through 3D space plus line through 1D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return VK_FORMAT_BC3_SRGB_BLOCK; // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB + + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: return VK_FORMAT_BC4_UNORM_BLOCK; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: return VK_FORMAT_BC5_UNORM_BLOCK; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: return VK_FORMAT_BC4_SNORM_BLOCK; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: return VK_FORMAT_BC5_SNORM_BLOCK; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RED_RGTC1: return VK_FORMAT_BC4_UNORM_BLOCK; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG_RGTC2: return VK_FORMAT_BC5_UNORM_BLOCK; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RED_RGTC1: return VK_FORMAT_BC4_SNORM_BLOCK; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG_RGTC2: return VK_FORMAT_BC5_SNORM_BLOCK; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: return VK_FORMAT_BC6H_UFLOAT_BLOCK; // 3-component, 4x4 blocks, unsigned floating-point + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: return VK_FORMAT_BC6H_SFLOAT_BLOCK; // 3-component, 4x4 blocks, signed floating-point + case GL_COMPRESSED_RGBA_BPTC_UNORM: return VK_FORMAT_BC7_UNORM_BLOCK; // 4-component, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: return VK_FORMAT_BC7_SRGB_BLOCK; // 4-component, 4x4 blocks, sRGB + + // + // ETC + // + case GL_ETC1_RGB8_OES: return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; // 3-component ETC1, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_RGB8_ETC2: return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; // 3-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA8_ETC2_EAC: return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; // 4-component ETC2, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ETC2: return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; // 3-component ETC2, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; // 4-component ETC2, 4x4 blocks, sRGB + + case GL_COMPRESSED_R11_EAC: return VK_FORMAT_EAC_R11_UNORM_BLOCK; // 1-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG11_EAC: return VK_FORMAT_EAC_R11G11_UNORM_BLOCK; // 2-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_R11_EAC: return VK_FORMAT_EAC_R11_SNORM_BLOCK; // 1-component ETC, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG11_EAC: return VK_FORMAT_EAC_R11G11_SNORM_BLOCK; // 2-component ETC, 4x4 blocks, signed normalized + + // + // PVRTC + // + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG; // 3-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG; // 3-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG; // 4-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG; // 4-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: return VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG; // 4-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: return VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG; // 4-component PVRTC, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG; // 3-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG; // 3-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG; // 4-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG; // 4-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: return VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG; // 4-component PVRTC, 8x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: return VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG; // 4-component PVRTC, 4x4 blocks, sRGB + + // + // ASTC + // + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: return VK_FORMAT_ASTC_4x4_UNORM_BLOCK; // 4-component ASTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: return VK_FORMAT_ASTC_5x4_UNORM_BLOCK; // 4-component ASTC, 5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: return VK_FORMAT_ASTC_5x5_UNORM_BLOCK; // 4-component ASTC, 5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: return VK_FORMAT_ASTC_6x5_UNORM_BLOCK; // 4-component ASTC, 6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: return VK_FORMAT_ASTC_6x6_UNORM_BLOCK; // 4-component ASTC, 6x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: return VK_FORMAT_ASTC_8x5_UNORM_BLOCK; // 4-component ASTC, 8x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: return VK_FORMAT_ASTC_8x6_UNORM_BLOCK; // 4-component ASTC, 8x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: return VK_FORMAT_ASTC_8x8_UNORM_BLOCK; // 4-component ASTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: return VK_FORMAT_ASTC_10x5_UNORM_BLOCK; // 4-component ASTC, 10x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: return VK_FORMAT_ASTC_10x6_UNORM_BLOCK; // 4-component ASTC, 10x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: return VK_FORMAT_ASTC_10x8_UNORM_BLOCK; // 4-component ASTC, 10x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: return VK_FORMAT_ASTC_10x10_UNORM_BLOCK; // 4-component ASTC, 10x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: return VK_FORMAT_ASTC_12x10_UNORM_BLOCK; // 4-component ASTC, 12x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: return VK_FORMAT_ASTC_12x12_UNORM_BLOCK; // 4-component ASTC, 12x12 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: return VK_FORMAT_ASTC_4x4_SRGB_BLOCK; // 4-component ASTC, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: return VK_FORMAT_ASTC_5x4_SRGB_BLOCK; // 4-component ASTC, 5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: return VK_FORMAT_ASTC_5x5_SRGB_BLOCK; // 4-component ASTC, 5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: return VK_FORMAT_ASTC_6x5_SRGB_BLOCK; // 4-component ASTC, 6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: return VK_FORMAT_ASTC_6x6_SRGB_BLOCK; // 4-component ASTC, 6x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: return VK_FORMAT_ASTC_8x5_SRGB_BLOCK; // 4-component ASTC, 8x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: return VK_FORMAT_ASTC_8x6_SRGB_BLOCK; // 4-component ASTC, 8x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: return VK_FORMAT_ASTC_8x8_SRGB_BLOCK; // 4-component ASTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: return VK_FORMAT_ASTC_10x5_SRGB_BLOCK; // 4-component ASTC, 10x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: return VK_FORMAT_ASTC_10x6_SRGB_BLOCK; // 4-component ASTC, 10x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: return VK_FORMAT_ASTC_10x8_SRGB_BLOCK; // 4-component ASTC, 10x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: return VK_FORMAT_ASTC_10x10_SRGB_BLOCK; // 4-component ASTC, 10x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: return VK_FORMAT_ASTC_12x10_SRGB_BLOCK; // 4-component ASTC, 12x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: return VK_FORMAT_ASTC_12x12_SRGB_BLOCK; // 4-component ASTC, 12x12 blocks, sRGB + + case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 3x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x6 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 3x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x6 blocks, sRGB + + // + // ATC + // + case GL_ATC_RGB_AMD: return VK_FORMAT_UNDEFINED; // 3-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: return VK_FORMAT_UNDEFINED; // 4-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: return VK_FORMAT_UNDEFINED; // 4-component, 4x4 blocks, unsigned normalized + + // + // Palletized + // + case GL_PALETTE4_RGB8_OES: return VK_FORMAT_UNDEFINED; // 3-component 8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA8_OES: return VK_FORMAT_UNDEFINED; // 4-component 8:8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_R5_G6_B5_OES: return VK_FORMAT_UNDEFINED; // 3-component 5:6:5, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA4_OES: return VK_FORMAT_UNDEFINED; // 4-component 4:4:4:4, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGB5_A1_OES: return VK_FORMAT_UNDEFINED; // 4-component 5:5:5:1, 4-bit palette, unsigned normalized + case GL_PALETTE8_RGB8_OES: return VK_FORMAT_UNDEFINED; // 3-component 8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA8_OES: return VK_FORMAT_UNDEFINED; // 4-component 8:8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_R5_G6_B5_OES: return VK_FORMAT_UNDEFINED; // 3-component 5:6:5, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA4_OES: return VK_FORMAT_UNDEFINED; // 4-component 4:4:4:4, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGB5_A1_OES: return VK_FORMAT_UNDEFINED; // 4-component 5:5:5:1, 8-bit palette, unsigned normalized + + // + // Depth/stencil + // + case GL_DEPTH_COMPONENT16: return VK_FORMAT_D16_UNORM; + case GL_DEPTH_COMPONENT24: return VK_FORMAT_X8_D24_UNORM_PACK32; + case GL_DEPTH_COMPONENT32: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT32F: return VK_FORMAT_D32_SFLOAT; + case GL_DEPTH_COMPONENT32F_NV: return VK_FORMAT_D32_SFLOAT; + case GL_STENCIL_INDEX1: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX4: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX8: return VK_FORMAT_S8_UINT; + case GL_STENCIL_INDEX16: return VK_FORMAT_UNDEFINED; + case GL_DEPTH24_STENCIL8: return VK_FORMAT_D24_UNORM_S8_UINT; + case GL_DEPTH32F_STENCIL8: return VK_FORMAT_D32_SFLOAT_S8_UINT; + case GL_DEPTH32F_STENCIL8_NV: return VK_FORMAT_D32_SFLOAT_S8_UINT; + + default: return VK_FORMAT_UNDEFINED; + } +} + +static inline void vkGetFormatSize( const VkFormat format, ktxFormatSize * pFormatSize ) +{ + pFormatSize->minBlocksX = pFormatSize->minBlocksY = 1; + switch ( format ) + { + case VK_FORMAT_R4G4_UNORM_PACK8: + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R4G4B4A4_UNORM_PACK16: + case VK_FORMAT_B4G4R4A4_UNORM_PACK16: + case VK_FORMAT_R5G6B5_UNORM_PACK16: + case VK_FORMAT_B5G6R5_UNORM_PACK16: + case VK_FORMAT_R5G5B5A1_UNORM_PACK16: + case VK_FORMAT_B5G5R5A1_UNORM_PACK16: + case VK_FORMAT_A1R5G5B5_UNORM_PACK16: + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R8_UNORM: + case VK_FORMAT_R8_SNORM: + case VK_FORMAT_R8_USCALED: + case VK_FORMAT_R8_SSCALED: + case VK_FORMAT_R8_UINT: + case VK_FORMAT_R8_SINT: + case VK_FORMAT_R8_SRGB: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R8G8_UNORM: + case VK_FORMAT_R8G8_SNORM: + case VK_FORMAT_R8G8_USCALED: + case VK_FORMAT_R8G8_SSCALED: + case VK_FORMAT_R8G8_UINT: + case VK_FORMAT_R8G8_SINT: + case VK_FORMAT_R8G8_SRGB: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R8G8B8_UNORM: + case VK_FORMAT_R8G8B8_SNORM: + case VK_FORMAT_R8G8B8_USCALED: + case VK_FORMAT_R8G8B8_SSCALED: + case VK_FORMAT_R8G8B8_UINT: + case VK_FORMAT_R8G8B8_SINT: + case VK_FORMAT_R8G8B8_SRGB: + case VK_FORMAT_B8G8R8_UNORM: + case VK_FORMAT_B8G8R8_SNORM: + case VK_FORMAT_B8G8R8_USCALED: + case VK_FORMAT_B8G8R8_SSCALED: + case VK_FORMAT_B8G8R8_UINT: + case VK_FORMAT_B8G8R8_SINT: + case VK_FORMAT_B8G8R8_SRGB: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 3 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8G8B8A8_SNORM: + case VK_FORMAT_R8G8B8A8_USCALED: + case VK_FORMAT_R8G8B8A8_SSCALED: + case VK_FORMAT_R8G8B8A8_UINT: + case VK_FORMAT_R8G8B8A8_SINT: + case VK_FORMAT_R8G8B8A8_SRGB: + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SNORM: + case VK_FORMAT_B8G8R8A8_USCALED: + case VK_FORMAT_B8G8R8A8_SSCALED: + case VK_FORMAT_B8G8R8A8_UINT: + case VK_FORMAT_B8G8R8A8_SINT: + case VK_FORMAT_B8G8R8A8_SRGB: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_A8B8G8R8_UNORM_PACK32: + case VK_FORMAT_A8B8G8R8_SNORM_PACK32: + case VK_FORMAT_A8B8G8R8_USCALED_PACK32: + case VK_FORMAT_A8B8G8R8_SSCALED_PACK32: + case VK_FORMAT_A8B8G8R8_UINT_PACK32: + case VK_FORMAT_A8B8G8R8_SINT_PACK32: + case VK_FORMAT_A8B8G8R8_SRGB_PACK32: + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_A2R10G10B10_UNORM_PACK32: + case VK_FORMAT_A2R10G10B10_SNORM_PACK32: + case VK_FORMAT_A2R10G10B10_USCALED_PACK32: + case VK_FORMAT_A2R10G10B10_SSCALED_PACK32: + case VK_FORMAT_A2R10G10B10_UINT_PACK32: + case VK_FORMAT_A2R10G10B10_SINT_PACK32: + case VK_FORMAT_A2B10G10R10_UNORM_PACK32: + case VK_FORMAT_A2B10G10R10_SNORM_PACK32: + case VK_FORMAT_A2B10G10R10_USCALED_PACK32: + case VK_FORMAT_A2B10G10R10_SSCALED_PACK32: + case VK_FORMAT_A2B10G10R10_UINT_PACK32: + case VK_FORMAT_A2B10G10R10_SINT_PACK32: + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R16_UNORM: + case VK_FORMAT_R16_SNORM: + case VK_FORMAT_R16_USCALED: + case VK_FORMAT_R16_SSCALED: + case VK_FORMAT_R16_UINT: + case VK_FORMAT_R16_SINT: + case VK_FORMAT_R16_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R16G16_UNORM: + case VK_FORMAT_R16G16_SNORM: + case VK_FORMAT_R16G16_USCALED: + case VK_FORMAT_R16G16_SSCALED: + case VK_FORMAT_R16G16_UINT: + case VK_FORMAT_R16G16_SINT: + case VK_FORMAT_R16G16_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R16G16B16_UNORM: + case VK_FORMAT_R16G16B16_SNORM: + case VK_FORMAT_R16G16B16_USCALED: + case VK_FORMAT_R16G16B16_SSCALED: + case VK_FORMAT_R16G16B16_UINT: + case VK_FORMAT_R16G16B16_SINT: + case VK_FORMAT_R16G16B16_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 6 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R16G16B16A16_UNORM: + case VK_FORMAT_R16G16B16A16_SNORM: + case VK_FORMAT_R16G16B16A16_USCALED: + case VK_FORMAT_R16G16B16A16_SSCALED: + case VK_FORMAT_R16G16B16A16_UINT: + case VK_FORMAT_R16G16B16A16_SINT: + case VK_FORMAT_R16G16B16A16_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R32_UINT: + case VK_FORMAT_R32_SINT: + case VK_FORMAT_R32_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R32G32_UINT: + case VK_FORMAT_R32G32_SINT: + case VK_FORMAT_R32G32_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R32G32B32_UINT: + case VK_FORMAT_R32G32B32_SINT: + case VK_FORMAT_R32G32B32_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 12 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R32G32B32A32_UINT: + case VK_FORMAT_R32G32B32A32_SINT: + case VK_FORMAT_R32G32B32A32_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R64_UINT: + case VK_FORMAT_R64_SINT: + case VK_FORMAT_R64_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R64G64_UINT: + case VK_FORMAT_R64G64_SINT: + case VK_FORMAT_R64G64_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R64G64B64_UINT: + case VK_FORMAT_R64G64B64_SINT: + case VK_FORMAT_R64G64B64_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 24 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R64G64B64A64_UINT: + case VK_FORMAT_R64G64B64A64_SINT: + case VK_FORMAT_R64G64B64A64_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_B10G11R11_UFLOAT_PACK32: + case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D16_UNORM: + pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_X8_D24_UNORM_PACK32: + pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT | KTX_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D32_SFLOAT: + pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_S8_UINT: + pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D16_UNORM_S8_UINT: + pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 3 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D24_UNORM_S8_UINT: + pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D32_SFLOAT_S8_UINT: + pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: + case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: + case VK_FORMAT_BC4_UNORM_BLOCK: + case VK_FORMAT_BC4_SNORM_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_BC2_UNORM_BLOCK: + case VK_FORMAT_BC2_SRGB_BLOCK: + case VK_FORMAT_BC3_UNORM_BLOCK: + case VK_FORMAT_BC3_SRGB_BLOCK: + case VK_FORMAT_BC5_UNORM_BLOCK: + case VK_FORMAT_BC5_SNORM_BLOCK: + case VK_FORMAT_BC6H_UFLOAT_BLOCK: + case VK_FORMAT_BC6H_SFLOAT_BLOCK: + case VK_FORMAT_BC7_UNORM_BLOCK: + case VK_FORMAT_BC7_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + case VK_FORMAT_EAC_R11_UNORM_BLOCK: + case VK_FORMAT_EAC_R11_SNORM_BLOCK: + case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: + case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + pFormatSize->minBlocksX = 2; + pFormatSize->minBlocksY = 2; + break; + case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + pFormatSize->minBlocksX = 2; + pFormatSize->minBlocksY = 2; + break; + case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: + case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: + case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: + case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: + case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: + case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 10; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: + case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 12; + pFormatSize->blockHeight = 10; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: + case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: + pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 12; + pFormatSize->blockHeight = 12; + pFormatSize->blockDepth = 1; + break; + default: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 0 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + } +} + +#endif // !VK_FORMAT_H diff --git a/thirdparty/libktx/lib/vkformat_enum.h b/thirdparty/libktx/lib/vkformat_enum.h new file mode 100644 index 0000000000..c6d85602bd --- /dev/null +++ b/thirdparty/libktx/lib/vkformat_enum.h @@ -0,0 +1,300 @@ +#if !defined(_VKFORMAT_ENUM_H_) && !defined(VULKAN_CORE_H_) +#define _VKFORMAT_ENUM_H_ + +/***************************** Do not edit. ***************************** + Automatically generated from vulkan_core.h version 151 by mkvkformatfiles. + *************************************************************************/ + +/* +** Copyright (c) 2015-2020 The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 +*/ + +#if defined(_MSC_VER) && _MSC_VER < 1900 // Older than VS 2015. +typedef unsigned __int32 VkFlags; +#else +#include <stdint.h> +typedef uint32_t VkFlags; +#endif + +typedef enum VkFormat { + VK_FORMAT_UNDEFINED = 0, + VK_FORMAT_R4G4_UNORM_PACK8 = 1, + VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, + VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, + VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, + VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, + VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, + VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, + VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, + VK_FORMAT_R8_UNORM = 9, + VK_FORMAT_R8_SNORM = 10, + VK_FORMAT_R8_USCALED = 11, + VK_FORMAT_R8_SSCALED = 12, + VK_FORMAT_R8_UINT = 13, + VK_FORMAT_R8_SINT = 14, + VK_FORMAT_R8_SRGB = 15, + VK_FORMAT_R8G8_UNORM = 16, + VK_FORMAT_R8G8_SNORM = 17, + VK_FORMAT_R8G8_USCALED = 18, + VK_FORMAT_R8G8_SSCALED = 19, + VK_FORMAT_R8G8_UINT = 20, + VK_FORMAT_R8G8_SINT = 21, + VK_FORMAT_R8G8_SRGB = 22, + VK_FORMAT_R8G8B8_UNORM = 23, + VK_FORMAT_R8G8B8_SNORM = 24, + VK_FORMAT_R8G8B8_USCALED = 25, + VK_FORMAT_R8G8B8_SSCALED = 26, + VK_FORMAT_R8G8B8_UINT = 27, + VK_FORMAT_R8G8B8_SINT = 28, + VK_FORMAT_R8G8B8_SRGB = 29, + VK_FORMAT_B8G8R8_UNORM = 30, + VK_FORMAT_B8G8R8_SNORM = 31, + VK_FORMAT_B8G8R8_USCALED = 32, + VK_FORMAT_B8G8R8_SSCALED = 33, + VK_FORMAT_B8G8R8_UINT = 34, + VK_FORMAT_B8G8R8_SINT = 35, + VK_FORMAT_B8G8R8_SRGB = 36, + VK_FORMAT_R8G8B8A8_UNORM = 37, + VK_FORMAT_R8G8B8A8_SNORM = 38, + VK_FORMAT_R8G8B8A8_USCALED = 39, + VK_FORMAT_R8G8B8A8_SSCALED = 40, + VK_FORMAT_R8G8B8A8_UINT = 41, + VK_FORMAT_R8G8B8A8_SINT = 42, + VK_FORMAT_R8G8B8A8_SRGB = 43, + VK_FORMAT_B8G8R8A8_UNORM = 44, + VK_FORMAT_B8G8R8A8_SNORM = 45, + VK_FORMAT_B8G8R8A8_USCALED = 46, + VK_FORMAT_B8G8R8A8_SSCALED = 47, + VK_FORMAT_B8G8R8A8_UINT = 48, + VK_FORMAT_B8G8R8A8_SINT = 49, + VK_FORMAT_B8G8R8A8_SRGB = 50, + VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, + VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, + VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, + VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, + VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, + VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, + VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, + VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, + VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, + VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, + VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, + VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, + VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, + VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, + VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, + VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, + VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, + VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, + VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, + VK_FORMAT_R16_UNORM = 70, + VK_FORMAT_R16_SNORM = 71, + VK_FORMAT_R16_USCALED = 72, + VK_FORMAT_R16_SSCALED = 73, + VK_FORMAT_R16_UINT = 74, + VK_FORMAT_R16_SINT = 75, + VK_FORMAT_R16_SFLOAT = 76, + VK_FORMAT_R16G16_UNORM = 77, + VK_FORMAT_R16G16_SNORM = 78, + VK_FORMAT_R16G16_USCALED = 79, + VK_FORMAT_R16G16_SSCALED = 80, + VK_FORMAT_R16G16_UINT = 81, + VK_FORMAT_R16G16_SINT = 82, + VK_FORMAT_R16G16_SFLOAT = 83, + VK_FORMAT_R16G16B16_UNORM = 84, + VK_FORMAT_R16G16B16_SNORM = 85, + VK_FORMAT_R16G16B16_USCALED = 86, + VK_FORMAT_R16G16B16_SSCALED = 87, + VK_FORMAT_R16G16B16_UINT = 88, + VK_FORMAT_R16G16B16_SINT = 89, + VK_FORMAT_R16G16B16_SFLOAT = 90, + VK_FORMAT_R16G16B16A16_UNORM = 91, + VK_FORMAT_R16G16B16A16_SNORM = 92, + VK_FORMAT_R16G16B16A16_USCALED = 93, + VK_FORMAT_R16G16B16A16_SSCALED = 94, + VK_FORMAT_R16G16B16A16_UINT = 95, + VK_FORMAT_R16G16B16A16_SINT = 96, + VK_FORMAT_R16G16B16A16_SFLOAT = 97, + VK_FORMAT_R32_UINT = 98, + VK_FORMAT_R32_SINT = 99, + VK_FORMAT_R32_SFLOAT = 100, + VK_FORMAT_R32G32_UINT = 101, + VK_FORMAT_R32G32_SINT = 102, + VK_FORMAT_R32G32_SFLOAT = 103, + VK_FORMAT_R32G32B32_UINT = 104, + VK_FORMAT_R32G32B32_SINT = 105, + VK_FORMAT_R32G32B32_SFLOAT = 106, + VK_FORMAT_R32G32B32A32_UINT = 107, + VK_FORMAT_R32G32B32A32_SINT = 108, + VK_FORMAT_R32G32B32A32_SFLOAT = 109, + VK_FORMAT_R64_UINT = 110, + VK_FORMAT_R64_SINT = 111, + VK_FORMAT_R64_SFLOAT = 112, + VK_FORMAT_R64G64_UINT = 113, + VK_FORMAT_R64G64_SINT = 114, + VK_FORMAT_R64G64_SFLOAT = 115, + VK_FORMAT_R64G64B64_UINT = 116, + VK_FORMAT_R64G64B64_SINT = 117, + VK_FORMAT_R64G64B64_SFLOAT = 118, + VK_FORMAT_R64G64B64A64_UINT = 119, + VK_FORMAT_R64G64B64A64_SINT = 120, + VK_FORMAT_R64G64B64A64_SFLOAT = 121, + VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, + VK_FORMAT_D16_UNORM = 124, + VK_FORMAT_X8_D24_UNORM_PACK32 = 125, + VK_FORMAT_D32_SFLOAT = 126, + VK_FORMAT_S8_UINT = 127, + VK_FORMAT_D16_UNORM_S8_UINT = 128, + VK_FORMAT_D24_UNORM_S8_UINT = 129, + VK_FORMAT_D32_SFLOAT_S8_UINT = 130, + VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, + VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, + VK_FORMAT_BC2_UNORM_BLOCK = 135, + VK_FORMAT_BC2_SRGB_BLOCK = 136, + VK_FORMAT_BC3_UNORM_BLOCK = 137, + VK_FORMAT_BC3_SRGB_BLOCK = 138, + VK_FORMAT_BC4_UNORM_BLOCK = 139, + VK_FORMAT_BC4_SNORM_BLOCK = 140, + VK_FORMAT_BC5_UNORM_BLOCK = 141, + VK_FORMAT_BC5_SNORM_BLOCK = 142, + VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, + VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, + VK_FORMAT_BC7_UNORM_BLOCK = 145, + VK_FORMAT_BC7_SRGB_BLOCK = 146, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, + VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, + VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, + VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, + VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, + VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, + VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, + VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, + VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, + VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, + VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, + VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, + VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, + VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000, + VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003, + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004, + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005, + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006, + VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009, + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010, + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016, + VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017, + VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018, + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019, + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020, + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026, + VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, + VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, + VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, + VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, + VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, + VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003, + VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004, + VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, + VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = 1000066000, + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = 1000066001, + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = 1000066002, + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = 1000066003, + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = 1000066004, + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = 1000066005, + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = 1000066006, + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = 1000066007, + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = 1000066008, + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = 1000066009, + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = 1000066010, + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = 1000066011, + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = 1000066012, + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = 1000066013, + VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT = 1000288000, + VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT = 1000288001, + VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT = 1000288002, + VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT = 1000288003, + VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT = 1000288004, + VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT = 1000288005, + VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT = 1000288006, + VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT = 1000288007, + VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT = 1000288008, + VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT = 1000288009, + VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT = 1000288010, + VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT = 1000288011, + VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT = 1000288012, + VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT = 1000288013, + VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT = 1000288014, + VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT = 1000288015, + VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT = 1000288016, + VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT = 1000288017, + VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT = 1000288018, + VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT = 1000288019, + VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT = 1000288020, + VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT = 1000288021, + VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT = 1000288022, + VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT = 1000288023, + VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT = 1000288024, + VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT = 1000288025, + VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT = 1000288026, + VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT = 1000288027, + VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT = 1000288028, + VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT = 1000288029, + VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT = 1000340000, + VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT = 1000340001, + VK_FORMAT_MAX_ENUM = 0x7FFFFFFF +} VkFormat; + +#define VK_FORMAT_MAX_STANDARD_ENUM 184 + +#endif /* _VKFORMAT_ENUM_H_ */ diff --git a/thirdparty/libktx/other_include/KHR/khrplatform.h b/thirdparty/libktx/other_include/KHR/khrplatform.h new file mode 100644 index 0000000000..5b55ea2b98 --- /dev/null +++ b/thirdparty/libktx/other_include/KHR/khrplatform.h @@ -0,0 +1,290 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are 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 Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include <KHR/khrplatform.h> + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC) + /* Win32 but not WinCE */ +# define KHRONOS_APIENTRY __stdcall +#else +# define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using <stdint.h> + */ +#include <stdint.h> +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using <inttypes.h> + */ +#include <inttypes.h> +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include <stdint.h> +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef _WIN64 +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ diff --git a/thirdparty/libktx/utils/unused.h b/thirdparty/libktx/utils/unused.h new file mode 100644 index 0000000000..31870ab639 --- /dev/null +++ b/thirdparty/libktx/utils/unused.h @@ -0,0 +1,37 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* Copyright 2019-2018 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* I'm extending this beyond the purpose implied by its name rather than creating + * a new file to hold the FALLTHROUGH declaration as this + * file is already included in most places FALLTHROUGH + * is needed. + */ + +#ifndef _UNUSED_H +#define _UNUSED_H + +#if (__cplusplus >= 201703L) +#define MAYBE_UNUSED [[maybe_unused]] +#elif __GNUC__ || __clang__ + #define MAYBE_UNUSED __attribute__((unused)) +#else + // Boohoo. VC++ has no equivalent + #define MAYBE_UNUSED +#endif + +#define U_ASSERT_ONLY MAYBE_UNUSED + +// For unused parameters of c functions. Portable. +#define UNUSED(x) (void)(x) + +#if !__clang__ && __GNUC__ // grumble ... clang ... grumble +#define FALLTHROUGH __attribute__((fallthrough)) +#else +#define FALLTHROUGH +#endif + +#endif /* UNUSED_H */ |