diff --git a/.github/workflows/build-dispatch.yaml b/.github/workflows/build-dispatch.yaml index d647c634d..f486b2c6b 100644 --- a/.github/workflows/build-dispatch.yaml +++ b/.github/workflows/build-dispatch.yaml @@ -11,6 +11,10 @@ on: description: "Target to be build. (separated by spaces)" required: true type: string + clang-version: + description: "Clang tools version to be installed" + required: false + type: string jobs: build: @@ -20,12 +24,24 @@ jobs: - name: Checkout Source Code uses: actions/checkout@v4 + - name: Setup Clang ${{ inputs.clang-version }} (ubuntu-only) + if: ${{ inputs.host-platform == 'ubuntu-latest' }} + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh ${{ inputs.clang-version }} + sudo apt install libc++-${{ inputs.clang-version }}-dev libc++abi-${{ inputs.clang-version }}-dev + - name: Setup Conan2 uses: iceshard-engine/.github/.github/actions/conan2@main with: conan-cache: true conan-cache-version: 'v1' conan-config-url: https://github.com/iceshard-engine/conan-config.git + conan-profile-compiler: 'clang' + conan-profile-compiler-version: '${{ inputs.clang-version }}' + conan-profile-cppstd: '20' + conan-profile-libcxx: 'libc++' - name: Setup IBT uses: iceshard-engine/.github/.github/actions/ibt-wks@main @@ -38,5 +54,5 @@ jobs: # Building the actual target - name: Build Target (${{ inputs.target }}) - shell: powershell + shell: pwsh run: ./ibt-ci build -t ${{ inputs.target }} diff --git a/.github/workflows/build-validate-code.yaml b/.github/workflows/build-validate-code.yaml index 94d870454..9228ff845 100644 --- a/.github/workflows/build-validate-code.yaml +++ b/.github/workflows/build-validate-code.yaml @@ -5,7 +5,10 @@ on: push: paths: - 'source/**' + - '.github/workflows/build-dispatch.yaml' + - '.github/workflows/build-validate-code.yaml' - '!source/data/**' + workflow_dispatch: jobs: validate: @@ -13,11 +16,14 @@ jobs: uses: ./.github/workflows/build-dispatch.yaml strategy: matrix: + host: [windows-latest, ubuntu-latest] project: [all] pipeline: [x64] config: [Debug,Develop,Profile,Release] include: - - host: windows-latest + - host: ubuntu-latest + clang-version: '20' with: host-platform: ${{ matrix.host }} target: "${{ matrix.project }}-${{ matrix.pipeline }}-${{ matrix.config }}" + clang-version: ${{ matrix.clang-version || '' }} diff --git a/.gitignore b/.gitignore index 2afe9931e..1e66a6815 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,10 @@ # Local data /source/data/temp /source/data/local -/tools/scripts/generate_version.moon +/tools/scripts/* + +# Specific scripts that would be nice to have in the repository +!/tools/scripts/start.moon # Temporary files to ignore compile_commands.json diff --git a/source/asset_compiler.bff b/source/asset_compiler.bff index a4baef08d..99f7571e9 100644 --- a/source/asset_compiler.bff +++ b/source/asset_compiler.bff @@ -1,11 +1,16 @@ - +/// Copyright 2024 - 2025, Dandielo +/// SPDX-License-Identifier: MIT .PlatformRule_GameAssets_ShadersMobile = [ .Name = 'Assets-Shaders-Mobile' .Requires = { 'assets-mobile', 'Step-Bake-Shaders' } .BuildOptions = { +#if __WINDOWS__ '-c shader_tools.dll' +#else // __LINUX__ + '-c libshader_tools.so' +#endif '--param shader:target GLSL' } ] @@ -15,7 +20,11 @@ .Name = 'Assets-Shaders-Web' .Requires = { 'assets-web', 'Step-Bake-Shaders' } .BuildOptions = { +#if __WINDOWS__ '-c shader_tools.dll' +#else // __LINUX__ + '-c libshader_tools.so' +#endif '--param shader:target WGSL' } ] diff --git a/source/code/core/collections/public/ice/container/impl/array_impl.inl b/source/code/core/collections/public/ice/container/impl/array_impl.inl index 98695d6d8..462b827f4 100644 --- a/source/code/core/collections/public/ice/container/impl/array_impl.inl +++ b/source/code/core/collections/public/ice/container/impl/array_impl.inl @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT namespace ice diff --git a/source/code/core/collections/public/ice/shard_container.hxx b/source/code/core/collections/public/ice/shard_container.hxx index a3a23aa1f..59e8c743f 100644 --- a/source/code/core/collections/public/ice/shard_container.hxx +++ b/source/code/core/collections/public/ice/shard_container.hxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/core/public/ice/assert_core.hxx b/source/code/core/core/public/ice/assert_core.hxx index 29bf1041c..1c2ad5908 100644 --- a/source/code/core/core/public/ice/assert_core.hxx +++ b/source/code/core/core/public/ice/assert_core.hxx @@ -16,7 +16,7 @@ (_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \ ); } } while(false) -#elif ISP_WEBAPP +#elif ISP_WEBAPP || ISP_LINUX #define ICE_ASSERT_CORE(expression) ((expression) \ ? (void)0 \ diff --git a/source/code/core/core/public/ice/build/build.hxx b/source/code/core/core/public/ice/build/build.hxx index 08e0bf207..d80344f48 100644 --- a/source/code/core/core/public/ice/build/build.hxx +++ b/source/code/core/core/public/ice/build/build.hxx @@ -22,12 +22,14 @@ namespace ice::build static constexpr bool is_windows = current_platform == System::Windows; - static constexpr bool is_unix = current_platform == System::Unix; + static constexpr bool is_linux = current_platform == System::Linux; static constexpr bool is_android = current_platform == System::Android; static constexpr bool is_webapp = current_platform == System::WebApp; + static constexpr bool is_unix = is_linux || is_android || is_webapp; + static constexpr bool is_x64 = current_platform == Architecture::x86_x64 || current_platform == Architecture::Arm64; diff --git a/source/code/core/core/public/ice/build/info.hxx b/source/code/core/core/public/ice/build/info.hxx index 17f101952..53cf76b64 100644 --- a/source/code/core/core/public/ice/build/info.hxx +++ b/source/code/core/core/public/ice/build/info.hxx @@ -1,3 +1,6 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + #pragma once #include diff --git a/source/code/core/core/public/ice/build/platform.hxx b/source/code/core/core/public/ice/build/platform.hxx index 93c76a005..447510787 100644 --- a/source/code/core/core/public/ice/build/platform.hxx +++ b/source/code/core/core/public/ice/build/platform.hxx @@ -13,7 +13,7 @@ namespace ice::build UWP, Windows, Android, - Unix, + Linux, WebApp }; @@ -109,20 +109,13 @@ namespace ice::build .compiler = Compiler::Clang }; - static constexpr Platform platform_unix_x64_clang = { - .name = "unix-x64-clang", - .system = System::Unix, + static constexpr Platform platform_linux_x64_clang = { + .name = "linux-x64-clang", + .system = System::Linux, .architecture = Architecture::x86_x64, .compiler = Compiler::Clang }; - static constexpr Platform platform_unix_x64_gcc = { - .name = "unix-x64-gcc", - .system = System::Unix, - .architecture = Architecture::x86_x64, - .compiler = Compiler::GCC - }; - static constexpr Platform platform_webapp_webasm32_clang = { .name = "webapp-webasm32-clang", .system = System::WebApp, @@ -136,8 +129,7 @@ namespace ice::build platform_windows_x64_clang, platform_android_arm64_clang, platform_android_x64_clang, - platform_unix_x64_clang, - platform_unix_x64_gcc, + platform_linux_x64_clang, platform_webapp_webasm32_clang }; @@ -152,6 +144,7 @@ namespace ice::build #if defined(_WIN64) # define ISP_UNIX 0 +# define ISP_LINUX 0 # define ISP_WINDOWS 1 # define ISP_ANDROID 0 # define ISP_WEBAPP 0 @@ -173,6 +166,7 @@ namespace ice::build # define ISP_ARCHFAM_WEBASM 0 #elif defined(__ANDROID__) # define ISP_UNIX 1 +# define ISP_LINUX 0 # define ISP_WINDOWS 0 # define ISP_ANDROID __ANDROID_API__ # define ISP_WEBAPP 0 @@ -193,6 +187,7 @@ namespace ice::build # endif #elif defined(EMSCRIPTEN) # define ISP_UNIX 1 +# define ISP_LINUX 0 # define ISP_WINDOWS 0 # define ISP_ANDROID 0 # define ISP_WEBAPP 1 @@ -207,6 +202,7 @@ namespace ice::build static constexpr Platform current_platform = platform_webapp_webasm32_clang; #elif __unix__ && !__clang__ # define ISP_UNIX 1 +# define ISP_LINUX 1 # define ISP_WINDOWS 0 # define ISP_ANDROID 0 # define ISP_WEBAPP 0 @@ -220,6 +216,7 @@ namespace ice::build static constexpr Platform current_platform = platform_unix_x64_gcc; #elif __unix__ && __clang__ # define ISP_UNIX 1 +# define ISP_LINUX 1 # define ISP_WINDOWS 0 # define ISP_ANDROID 0 # define ISP_WEBAPP 0 @@ -230,7 +227,7 @@ namespace ice::build # define ISP_ARCHFAM_ARM 0 # define ISP_ARCHFAM_WEBASM 0 - static constexpr Platform current_platform = platform_unix_x64_clang; + static constexpr Platform current_platform = platform_linux_x64_clang; #else # define ISP_UNIX 0 # define ISP_WINDOWS 0 @@ -321,8 +318,8 @@ namespace ice::build return "windows"; case ice::build::System::Android: return "android"; - case ice::build::System::Unix: - return "unix"; + case ice::build::System::Linux: + return "linux"; default: return ""; } diff --git a/source/code/core/core/public/ice/clock_types.hxx b/source/code/core/core/public/ice/clock_types.hxx index 63c230c29..dc3bf3341 100644 --- a/source/code/core/core/public/ice/clock_types.hxx +++ b/source/code/core/core/public/ice/clock_types.hxx @@ -251,5 +251,4 @@ namespace ice } // namespace detail::ct_tests - } // namespace ice diff --git a/source/code/core/core/public/ice/os.hxx b/source/code/core/core/public/ice/os.hxx index f583dac23..f98f8dcbd 100644 --- a/source/code/core/core/public/ice/os.hxx +++ b/source/code/core/core/public/ice/os.hxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/core/public/ice/os/windows.hxx b/source/code/core/core/public/ice/os/windows.hxx index 56b842e3b..e98790aec 100644 --- a/source/code/core/core/public/ice/os/windows.hxx +++ b/source/code/core/core/public/ice/os/windows.hxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/memsys/private/mem.cxx b/source/code/core/memsys/private/mem.cxx index d6f774e68..3739272b4 100644 --- a/source/code/core/memsys/private/mem.cxx +++ b/source/code/core/memsys/private/mem.cxx @@ -46,7 +46,7 @@ namespace ice .size = size, .alignment = alignment, }; -#elif ISP_WEBAPP || (ISP_ANDROID && ISP_ANDROID <= 29) +#elif ISP_LINUX || ISP_WEBAPP || (ISP_ANDROID && ISP_ANDROID <= 29) void* memory_location; [[maybe_unused]] diff --git a/source/code/core/memsys/private/mem_allocator.cxx b/source/code/core/memsys/private/mem_allocator.cxx index a3996f1ae..6ad89cf8d 100644 --- a/source/code/core/memsys/private/mem_allocator.cxx +++ b/source/code/core/memsys/private/mem_allocator.cxx @@ -169,7 +169,8 @@ namespace ice auto AllocatorBase::allocate(ice::AllocRequest request) noexcept -> ice::AllocResult { - ICE_ASSERT_CORE(request.size != 0_B); + // TODO: Check if requesting sizes of '0' can be actually allowed + // ICE_ASSERT_CORE(request.size != 0_B); ice::AllocResult result = do_allocate(request); _internal->insert(result); diff --git a/source/code/core/memsys/public/ice/mem_allocator_host.hxx b/source/code/core/memsys/public/ice/mem_allocator_host.hxx index ef06615f5..605032aec 100644 --- a/source/code/core/memsys/public/ice/mem_allocator_host.hxx +++ b/source/code/core/memsys/public/ice/mem_allocator_host.hxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/modules/private/module.cxx b/source/code/core/modules/private/module.cxx index f492411d4..d0eaafd37 100644 --- a/source/code/core/modules/private/module.cxx +++ b/source/code/core/modules/private/module.cxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #include diff --git a/source/code/core/modules/private/module_globals.cxx b/source/code/core/modules/private/module_globals.cxx index c2ca5e4dc..478aa8a8d 100644 --- a/source/code/core/modules/private/module_globals.cxx +++ b/source/code/core/modules/private/module_globals.cxx @@ -61,7 +61,7 @@ extern "C" } // extern "C" -#elif ISP_ANDROID +#elif ISP_ANDROID || ISP_LINUX extern "C" { diff --git a/source/code/core/modules/private/module_globals.hxx b/source/code/core/modules/private/module_globals.hxx index efe0945fe..fc8fe9342 100644 --- a/source/code/core/modules/private/module_globals.hxx +++ b/source/code/core/modules/private/module_globals.hxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/modules/private/module_native.cxx b/source/code/core/modules/private/module_native.cxx index 839f74970..57e9ec351 100644 --- a/source/code/core/modules/private/module_native.cxx +++ b/source/code/core/modules/private/module_native.cxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #include "module_native.hxx" diff --git a/source/code/core/modules/private/module_native.hxx b/source/code/core/modules/private/module_native.hxx index 0de1ef5ce..7ebcec4fd 100644 --- a/source/code/core/modules/private/module_native.hxx +++ b/source/code/core/modules/private/module_native.hxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/modules/public/ice/module_info.hxx b/source/code/core/modules/public/ice/module_info.hxx index a4df9e8fa..fba1effe0 100644 --- a/source/code/core/modules/public/ice/module_info.hxx +++ b/source/code/core/modules/public/ice/module_info.hxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/modules/public/ice/module_query.hxx b/source/code/core/modules/public/ice/module_query.hxx index c13f50e15..d5d51c558 100644 --- a/source/code/core/modules/public/ice/module_query.hxx +++ b/source/code/core/modules/public/ice/module_query.hxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/modules/public/ice/module_register.hxx b/source/code/core/modules/public/ice/module_register.hxx index beb8a49fb..4bda2ed1b 100644 --- a/source/code/core/modules/public/ice/module_register.hxx +++ b/source/code/core/modules/public/ice/module_register.hxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/tasks/private/task_native_thread.cxx b/source/code/core/tasks/private/task_native_thread.cxx index d820dd325..226b48a13 100644 --- a/source/code/core/tasks/private/task_native_thread.cxx +++ b/source/code/core/tasks/private/task_native_thread.cxx @@ -326,6 +326,11 @@ namespace ice { emscripten_set_thread_name(pthread_self(), ice::string::begin(thread_info.debug_name)); } +#elif ISP_LINUX + if constexpr (ice::build::is_release == false) + { + pthread_setname_np(pthread_self(), ice::string::begin(thread_info.debug_name)); + } #endif ice::ThreadRuntime& runtime = thread_obj->runtime(); @@ -380,7 +385,7 @@ namespace ice { error = pthread_attr_setstacksize( &thread_attribs, - ice::max(ice::usize::base_type{PTHREAD_STACK_MIN}, info.stack_size.value) + ice::max(ice::usize::base_type(PTHREAD_STACK_MIN), info.stack_size.value) ); ICE_ASSERT( error == 0, diff --git a/source/code/core/tasks/private/task_thread_utils.cxx b/source/code/core/tasks/private/task_thread_utils.cxx new file mode 100644 index 000000000..658944a02 --- /dev/null +++ b/source/code/core/tasks/private/task_thread_utils.cxx @@ -0,0 +1,21 @@ +#include +#include +#include + +namespace ice::current_thread +{ + + void sleep(Tms ms) noexcept + { +#if ISP_WINDOWS + ICE_ASSERT_CORE(ms.value <= ice::u32_max); + SleepEx(DWORD(ms.value), 0); +#elif ISP_UNIX + Tus const us = ms; + usleep(us.value); +#else + ICE_ASSERT_CORE(false); +#endif + } + +} // namespace ice::current_thread diff --git a/source/code/core/tasks/public/ice/task_cancelation_token.hxx b/source/code/core/tasks/public/ice/task_cancelation_token.hxx index 4da1a9c81..f8d6e5c9c 100644 --- a/source/code/core/tasks/public/ice/task_cancelation_token.hxx +++ b/source/code/core/tasks/public/ice/task_cancelation_token.hxx @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/tasks/public/ice/task_info.hxx b/source/code/core/tasks/public/ice/task_info.hxx index 1b6dbe8f1..2a6510a32 100644 --- a/source/code/core/tasks/public/ice/task_info.hxx +++ b/source/code/core/tasks/public/ice/task_info.hxx @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/core/tasks/public/ice/task_thread_utils.hxx b/source/code/core/tasks/public/ice/task_thread_utils.hxx new file mode 100644 index 000000000..2ab6c7143 --- /dev/null +++ b/source/code/core/tasks/public/ice/task_thread_utils.hxx @@ -0,0 +1,11 @@ +#pragma once +#include +#include + +namespace ice::current_thread +{ + + //! \brief Sleep for the selecter number of milliseconds. + void sleep(Tms ms) noexcept; + +} // namespace ice::current_thread diff --git a/source/code/core/utils/private/assert.cxx b/source/code/core/utils/private/assert.cxx index 40b9aaa2b..916c1cd42 100644 --- a/source/code/core/utils/private/assert.cxx +++ b/source/code/core/utils/private/assert.cxx @@ -20,9 +20,9 @@ namespace ice::detail { - static constexpr ice::String LogFormat_AssertLineHeader = "{:%T} [ASRT] ASSERT"; - static constexpr ice::String LogFormat_AssertCondition = "{}({}) : {} > Assertion failed! `{}`\n"; - static constexpr ice::String LogFormat_AssertMessage = "{}({}) : {} | "; + static constexpr fmt::string_view LogFormat_AssertLineHeader = "{:%T} [ASRT] ASSERT"; + static constexpr fmt::string_view LogFormat_AssertCondition = "{}({}) : {} > Assertion failed! `{}`\n"; + static constexpr fmt::string_view LogFormat_AssertMessage = "{}({}) : {} | "; void assert( ice::String condition, @@ -55,7 +55,7 @@ namespace ice::detail fmt::format_to_n_result format_result = fmt::format_to_n( header_buffer_raw, 128, - fmt_string(LogFormat_AssertLineHeader), + LogFormat_AssertLineHeader, fmt::localtime(std::time(nullptr)) ); @@ -69,25 +69,21 @@ namespace ice::detail static ice::HostAllocator host_alloc{}; detail::LogMessageBuffer final_buffer{ host_alloc, 3000 }; - fmt::vformat_to( + fmt::format_to( std::back_inserter(final_buffer), - fmt_string(LogFormat_AssertCondition), - fmt::make_format_args( - fmt_string(location.file), - location.line, - log_header, - fmt_string(condition) - ) + LogFormat_AssertCondition, + location.file, + location.line, + log_header, + condition ); - fmt::vformat_to( + fmt::format_to( std::back_inserter(final_buffer), - fmt_string(LogFormat_AssertMessage), - fmt::make_format_args( - fmt_string(location.file), - location.line, - log_header - ) + LogFormat_AssertMessage, + fmt_string(location.file), + location.line, + log_header ); fmt::vformat_to( @@ -118,7 +114,7 @@ namespace ice::detail ice::detail::LogLocation location ) noexcept { - fmt::vprint( + fmt::vprintln( stderr, fmt_string(message), ice::move(args) diff --git a/source/code/core/utils/private/config/config_builder_types.cxx b/source/code/core/utils/private/config/config_builder_types.cxx index a45eb8b24..53cf8a68d 100644 --- a/source/code/core/utils/private/config/config_builder_types.cxx +++ b/source/code/core/utils/private/config/config_builder_types.cxx @@ -27,8 +27,9 @@ namespace ice::config::detail { this->~ConfigBuilderContainer(); alloc.deallocate(this); + return true; } - return _refcount == 0; + return false; } void ConfigBuilderContainer::clear() noexcept diff --git a/source/code/core/utils/private/config_json.cxx b/source/code/core/utils/private/config_json.cxx index 900ed6498..6a40bdcac 100644 --- a/source/code/core/utils/private/config_json.cxx +++ b/source/code/core/utils/private/config_json.cxx @@ -4,6 +4,10 @@ #include #include +#if ISP_WINDOWS +#undef GetObject +#endif + ISC_WARNING_PUSH ISCW_OPERATOR_DEPRECATED_BETWEEN_UNRELATED_ENUMERATIONS(ISCW_OP_DISABLE) #include diff --git a/source/code/core/utils/private/log_android.cxx b/source/code/core/utils/private/log_android.cxx index 8802269bd..76bf40cac 100644 --- a/source/code/core/utils/private/log_android.cxx +++ b/source/code/core/utils/private/log_android.cxx @@ -8,6 +8,7 @@ #if ISP_ANDROID #include #include +#include namespace ice::detail::android { @@ -93,10 +94,10 @@ namespace ice::detail::android ice::move(args) ); - fmt::vformat_to( + fmt::format_to( std::back_inserter(final_buffer), fmt_string(LogFormat_AssertCondition), - fmt::make_format_args(fmt_string(condition)) + fmt_string(condition) ); char* last_char = final_buffer.end() - 1; diff --git a/source/code/core/utils/private/log_buffer.cxx b/source/code/core/utils/private/log_buffer.cxx index 10470de8e..296c9a74f 100644 --- a/source/code/core/utils/private/log_buffer.cxx +++ b/source/code/core/utils/private/log_buffer.cxx @@ -6,8 +6,13 @@ namespace ice::detail { + void internal_grow_fmt_buffer(fmt::detail::buffer& buf, size_t capacity) noexcept + { + static_cast(buf).grow(capacity); + } + LogMessageBuffer::LogMessageBuffer(ice::Allocator& alloc, ice::ucount initial_allocation) noexcept - : fmt::detail::buffer{ } + : fmt::detail::buffer{ internal_grow_fmt_buffer } , _allocator{ alloc } { this->set( diff --git a/source/code/core/utils/private/log_buffer.hxx b/source/code/core/utils/private/log_buffer.hxx index ad7cbb80b..7c0b8207f 100644 --- a/source/code/core/utils/private/log_buffer.hxx +++ b/source/code/core/utils/private/log_buffer.hxx @@ -9,13 +9,13 @@ namespace ice::detail { - class LogMessageBuffer final : public fmt::v10::detail::buffer + class LogMessageBuffer final : public fmt::v11::detail::buffer { public: LogMessageBuffer(ice::Allocator& alloc, ice::ucount initial_allocation) noexcept; ~LogMessageBuffer() noexcept; - void grow(size_t size) noexcept override; + void grow(size_t size) noexcept; private: ice::Allocator& _allocator; diff --git a/source/code/core/utils/private/log_webasm.cxx b/source/code/core/utils/private/log_webasm.cxx index 3a4253898..f70c4e5b5 100644 --- a/source/code/core/utils/private/log_webasm.cxx +++ b/source/code/core/utils/private/log_webasm.cxx @@ -20,15 +20,15 @@ namespace ice::detail::webasm throw "Failed assertion!"; }); - static constexpr ice::String LogFormat_AssertLineHeader = "{:%T} [ASRT] ASSERT"; - static constexpr ice::String LogFormat_AssertCondition = "{} > Assertion failed! `{}`\n"; - static constexpr ice::String LogFormat_AssertMessage = "{} | "; + static constexpr fmt::string_view LogFormat_AssertLineHeader = "{:%T} [ASRT] ASSERT"; + static constexpr fmt::string_view LogFormat_AssertCondition = "{} > Assertion failed! `{}`\n"; + static constexpr fmt::string_view LogFormat_AssertMessage = "{} | "; - static constexpr ice::String LogFormat_AlertLocation = "{}({})\n\n"; - static constexpr ice::String LogFormat_AlertCondition = "Failed condition: `{}`\n\n"; + static constexpr fmt::string_view LogFormat_AlertLocation = "{}({})\n\n"; + static constexpr fmt::string_view LogFormat_AlertCondition = "Failed condition: `{}`\n\n"; - static constexpr ice::String LogFormat_LogLineHeader = "{:%T} [{}] {}{}{}"; - static constexpr ice::String LogFormat_LogLine = "{: <{}s} > "; + static constexpr fmt::string_view LogFormat_LogLineHeader = "{:%T} [{}] {}{}{}"; + static constexpr fmt::string_view LogFormat_LogLine = "{: <{}s} > "; auto get_tag_name(ice::LogTag tag) noexcept -> ice::String { @@ -73,7 +73,7 @@ namespace ice::detail::webasm fmt::format_to_n_result format_result = fmt::format_to_n( header_buffer_raw, 128, - fmt_string(LogFormat_AssertLineHeader), + LogFormat_AssertLineHeader, fmt::localtime(std::time(nullptr)) ); @@ -87,21 +87,17 @@ namespace ice::detail::webasm static ice::HostAllocator host_alloc{}; detail::LogMessageBuffer final_buffer{ host_alloc, 3000 }; - fmt::vformat_to( + fmt::format_to( std::back_inserter(final_buffer), - fmt_string(LogFormat_AssertCondition), - fmt::make_format_args( - log_header, - fmt_string(condition) - ) + LogFormat_AssertCondition, + log_header, + fmt_string(condition) ); - fmt::vformat_to( + fmt::format_to( std::back_inserter(final_buffer), - fmt_string(LogFormat_AssertMessage), - fmt::make_format_args( - log_header - ) + LogFormat_AssertMessage, + log_header ); fmt::vformat_to( @@ -120,19 +116,17 @@ namespace ice::detail::webasm emscripten_console_error(final_buffer.data()); final_buffer.clear(); - fmt::vformat_to( + fmt::format_to( std::back_inserter(final_buffer), - fmt_string(LogFormat_AlertLocation), - fmt::make_format_args( - fmt_string(location.file), - location.line - ) + LogFormat_AlertLocation, + fmt_string(location.file), + location.line ); - fmt::vformat_to( + fmt::format_to( std::back_inserter(final_buffer), - fmt_string(LogFormat_AlertCondition), - fmt::make_format_args(fmt_string(condition)) + LogFormat_AlertCondition, + fmt_string(condition) ); fmt::vformat_to( @@ -165,7 +159,7 @@ namespace ice::detail::webasm fmt::format_to_n_result format_result = fmt::format_to_n( header_buffer_raw, 256, - fmt_string(LogFormat_LogLineHeader), + LogFormat_LogLineHeader, fmt::localtime(std::time(nullptr)), fmt_string(detail::severity_value[static_cast(severity)]), fmt_string(base_tag_name), @@ -185,7 +179,7 @@ namespace ice::detail::webasm fmt::vformat_to( std::back_inserter(final_buffer), - fmt_string(LogFormat_LogLine), + LogFormat_LogLine, fmt::make_format_args(log_header, LogState::minimal_header_length) ); diff --git a/source/code/core/utils/private/native_aio.cxx b/source/code/core/utils/private/native_aio.cxx index 478340e48..f9d84991d 100644 --- a/source/code/core/utils/private/native_aio.cxx +++ b/source/code/core/utils/private/native_aio.cxx @@ -235,7 +235,7 @@ namespace ice::native_aio request->_callback(result, read_size, request->_userdata); } } -#elif ISP_WEBAPP || ISP_ANDROID +#elif ISP_WEBAPP || ISP_ANDROID || ISP_LINUX auto aio_open( ice::Allocator& alloc, ice::native_aio::AIOPortInfo const& info @@ -295,11 +295,12 @@ namespace ice::native_aio ice::Memory memory ) noexcept -> ice::native_file::FileRequestStatus { + ICE_ASSERT_CORE(memory.location != nullptr); AIORequestInternal& internal = reinterpret_cast(request); internal.next = nullptr; internal.native_file_handle = file.native(); internal.request_type = 1; // 1 = read, 2 = write - internal.data_location = memory.location; + internal.data_destination = memory.location; internal.data_offset = static_cast(requested_read_offset.value); internal.data_size = static_cast(requested_read_size.value); ice::linked_queue::push(request._port->_requests, ice::addressof(internal)); @@ -309,6 +310,40 @@ namespace ice::native_aio return ice::native_file::FileRequestStatus::Pending; } + auto aio_file_write_request( + ice::native_aio::AIORequest& request, + ice::native_file::File const& file, + ice::usize requested_write_offset, + ice::Data data + ) noexcept -> ice::native_file::FileRequestStatus + { + using ice::native_file::FileRequestStatus; + + if (data.size == 0_B) + { + return FileRequestStatus::Completed; + } + + if (data.size.value > ice::u32_max) + { + // Too big of a write + return FileRequestStatus::Error; + } + + AIORequestInternal& internal = reinterpret_cast(request); + internal.next = nullptr; + internal.native_file_handle = file.native(); + internal.request_type = 2; // 1 = read, 2 = write + internal.data_location = data.location; + internal.data_size = ice::u32(data.size.value); + internal.data_offset = ice::u32(requested_write_offset.value); + ice::linked_queue::push(request._port->_requests, ice::addressof(internal)); + + // Increment the semaphore + sem_post(&request._port->_semaphore); + return ice::native_file::FileRequestStatus::Pending; + } + bool aio_file_await_request( ice::native_aio::AIOPort port, ice::native_aio::AIOProcessLimits limits, @@ -343,36 +378,48 @@ namespace ice::native_aio if (internal->request_type == 1) { // TODO: Handle sizes / offsets above 4gb - ice::i32 const bytes_read = pread( + ssize_t const bytes_read = pread( internal->native_file_handle, - internal->data_location, + internal->data_destination, internal->data_size, internal->data_offset ); ICE_ASSERT_CORE(bytes_read > 0); // 0 == eof, -1 == error ICE_ASSERT_CORE(bytes_read == internal->data_size); - out_size = { (ice::usize::base_type) bytes_read }; } else { - // TODO: Write requests - ICE_ASSERT_CORE(false); + ssize_t const bytes_written = pwrite( + internal->native_file_handle, + internal->data_location, + internal->data_size, + internal->data_offset + ); + + ICE_ASSERT_CORE(bytes_written > 0); + ICE_ASSERT_CORE(bytes_written == internal->data_size); + out_size = { (ice::usize::base_type) bytes_written }; } } + else + { + // Repost the semaphore, because we missed a request. + sem_post(&port->_semaphore); + } return out_size > 0_B; } void aio_complete_request( ice::native_aio::AIORequest const* request, ice::native_aio::AIORequestResult result, - ice::usize read_size + ice::usize processed_bytes ) noexcept { if (request != nullptr && request->_callback != nullptr) { - request->_callback(result, read_size, request->_userdata); + request->_callback(result, processed_bytes, request->_userdata); } } #else diff --git a/source/code/core/utils/private/native_aio.hxx b/source/code/core/utils/private/native_aio.hxx index 4c0d943c6..787f72e7f 100644 --- a/source/code/core/utils/private/native_aio.hxx +++ b/source/code/core/utils/private/native_aio.hxx @@ -18,13 +18,17 @@ namespace ice::native_aio HANDLE _completion_port; ice::u32 _worker_limit; }; -#elif ISP_ANDROID || ISP_WEBAPP +#elif ISP_ANDROID || ISP_WEBAPP || ISP_LINUX struct AIORequestInternal { AIORequestInternal* next; ice::i32 native_file_handle; ice::u32 request_type; // 1 == read, 2 == write - void* data_location; + union + { + void* data_destination; + void const* data_location; + }; ice::u32 data_offset; ice::u32 data_size; }; diff --git a/source/code/core/utils/private/native_file.cxx b/source/code/core/utils/private/native_file.cxx index 35712c79e..067122d16 100644 --- a/source/code/core/utils/private/native_file.cxx +++ b/source/code/core/utils/private/native_file.cxx @@ -267,6 +267,7 @@ namespace ice::native_file ice::Data data ) noexcept -> ice::usize { + IPT_ZONE_SCOPED; ice::usize total_written = 0_B; OVERLAPPED overlapped{}; @@ -477,6 +478,11 @@ namespace ice::native_file return create_directory_internal(actual_path); } + bool is_directory(ice::native_file::FilePath path) noexcept + { + return false; + } + void path_from_string( ice::native_file::HeapFilePath& out_filepath, ice::String path_string @@ -513,9 +519,27 @@ namespace ice::native_file #elif ISP_UNIX + static constexpr ice::i32 Constant_ModeDirectory = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + static constexpr ice::i32 Constant_ModeFile = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH; + inline auto translate_flags(ice::native_file::FileOpenFlags flags) noexcept -> int { - int result = O_RDONLY; + int result = O_RDONLY; // By default support large fils on linux Support + if (ice::has_any(flags, FileOpenFlags::Write)) + { + result = O_WRONLY; + if (ice::has_any(flags, FileOpenFlags::Read)) + { + result = O_RDWR; + } + + // Also ensure the file exists when writing + result |= O_CREAT; + } + if constexpr (ice::build::is_linux) + { + result |= O_LARGEFILE; + } #if 0 // TODO: Not supported yet if (ice::has_any(flags, FileOpenFlags::Asynchronous)) { @@ -537,16 +561,10 @@ namespace ice::native_file ) noexcept -> ice::native_file::File { ice::native_file::File result; - if constexpr (ice::build::current_platform == ice::build::System::Android) - { - result = ice::native_file::File{ - open(ice::string::begin(path), translate_flags(flags)) - }; - } - else if constexpr (ice::build::current_platform == ice::build::System::WebApp) + if constexpr (ice::build::is_unix) { result = ice::native_file::File{ - open(ice::string::begin(path), translate_flags(flags)) + open(ice::string::begin(path), translate_flags(flags), Constant_ModeFile) }; } else @@ -577,15 +595,21 @@ namespace ice::native_file auto sizeof_file(ice::native_file::File const& native_file) noexcept -> ice::usize { struct stat file_stats; - fstat(native_file.native(), &file_stats); - return { static_cast(file_stats.st_size) }; + if (fstat(native_file.native(), &file_stats) == 0) + { + return { static_cast(file_stats.st_size) }; + } + return 0_B; } auto sizeof_file(ice::native_file::FilePath path) noexcept -> ice::usize { struct stat file_stats; - stat(ice::string::begin(path), &file_stats); - return { static_cast(file_stats.st_size) }; + if (stat(ice::string::begin(path), &file_stats) == 0) + { + return { static_cast(file_stats.st_size) }; + } + return 0_B; } auto read_file( @@ -597,17 +621,6 @@ namespace ice::native_file return read_file(native_file, 0_B, requested_read_size, memory); } - auto read_file_request( - ice::native_aio::AIORequest& request, - ice::native_file::File const& native_file, - ice::usize requested_read_offset, - ice::usize requested_read_size, - ice::Memory memory - ) noexcept -> ice::native_file::FileRequestStatus - { - return aio_file_read_request(request, native_file, requested_read_offset, requested_read_size, memory); - } - auto read_file( ice::native_file::File const& native_file, ice::usize requested_read_offset, @@ -630,6 +643,56 @@ namespace ice::native_file return { static_cast(bytes_read) }; } + auto read_file_request( + ice::native_aio::AIORequest& request, + ice::native_file::File const& native_file, + ice::usize requested_read_offset, + ice::usize requested_read_size, + ice::Memory memory + ) noexcept -> ice::native_file::FileRequestStatus + { + return aio_file_read_request(request, native_file, requested_read_offset, requested_read_size, memory); + } + + auto write_file( + ice::native_file::File const& native_file, + ice::usize write_offset, + ice::Data data + ) noexcept -> ice::usize + { + IPT_ZONE_SCOPED; + + ice::usize written = 0_B; + if (data.size > 0_B) + { + ssize_t const result = pwrite(native_file.native(), data.location, data.size.value, write_offset.value); + ICE_ASSERT(result >= 0, "Failed to write {:i} bytes to file.", data.size); + if (result >= 0) + { + written.value = result; + } + } + return written; + } + + auto write_file_request( + ice::native_aio::AIORequest& request, + ice::native_file::File const& native_file, + ice::usize write_offset, + ice::Data data + ) noexcept -> ice::native_file::FileRequestStatus + { + return aio_file_write_request(request, native_file, write_offset, data); + } + + auto append_file( + ice::native_file::File const& native_file, + ice::Data data + ) noexcept -> ice::usize + { + return ice::native_file::write_file(native_file, ice::native_file::sizeof_file(native_file), data); + } + bool traverse_directories_internal( ice::native_file::FilePath basepath, ice::native_file::HeapFilePath& dirpath, @@ -714,6 +777,66 @@ namespace ice::native_file return traverse_directories_internal(dirpath, dirpath, callback, userdata); } + + bool create_directory_internal( + ice::native_file::HeapFilePath& dirpath + ) noexcept + { + // If zero, we failed, check why. + if (mkdir(ice::string::begin(dirpath), Constant_ModeDirectory) == -1) + { + // Try the next path before retrying this path. + if (errno == EEXIST) + { + return ice::native_file::is_directory(dirpath); + } + else if (errno == ENOENT) + { + // Remove the top-most the directory explicitly. + ice::ucount const dirslash = ice::string::find_last_of(dirpath, ice::String{ "/" }); + if (dirslash == ice::String_NPos) + { + return false; + } + + // Insert the new '0' to shorten the path for the Windows API. + dirpath[dirslash] = '\0'; + if (create_directory_internal(dirpath) == false) + { + return false; + } + + // 'Restore' the original path. (we assume it was normalized for iceshard) + dirpath[dirslash] = '/'; + + // Try again to create the directory + return mkdir(ice::string::begin(dirpath), Constant_ModeDirectory) != -1; + } + // else it's either 'ERROR_ALREADY_EXISTS' so we continue. + } + return true; + } + + bool create_directory( + ice::native_file::FilePath path + ) noexcept + { + ice::StackAllocator_1024 temp_alloc; + // We need to make a local string version, because the windows API does only accept zero-terimanated strings. + // However IceShard uses string views, which might result in strings being "longer" than intended. + ice::native_file::HeapFilePath actual_path{ temp_alloc, path }; + return create_directory_internal(actual_path); + } + + bool is_directory(ice::native_file::FilePath path) noexcept + { + struct stat info; + if (stat(ice::string::begin(path), &info) != 0) { + return false; + } + return S_ISDIR(info.st_mode); + } + void path_from_string( ice::native_file::HeapFilePath& out_filepath, ice::String path_string @@ -735,7 +858,14 @@ namespace ice::native_file ice::String string ) noexcept { - ice::path::join(path, string); + if (ice::string::any(path)) + { + ice::path::join(path, string); + } + else if (ice::string::any(string)) + { + path = string; + } } #else diff --git a/source/code/core/utils/private/params.cxx b/source/code/core/utils/private/params.cxx index 2a79fc8ac..93af4206b 100644 --- a/source/code/core/utils/private/params.cxx +++ b/source/code/core/utils/private/params.cxx @@ -227,17 +227,39 @@ namespace ice } } +#if ISP_LINUX + static auto f = [](std::string p) noexcept -> std::string + { + size_t const start_quote = p.find_first_of('"'); + size_t const end_quote = p.find_last_of('"'); + if (start_quote != std::string::npos && end_quote != std::string::npos && start_quote < end_quote) + { + return p.substr(start_quote + 1, (end_quote - start_quote) - 1); + } + return p; + }; +#endif + // Built-In Validators if (ice::has_all(definition.flags, PF::ValidatePath)) { +#if ISP_LINUX + opt->transform(f); +#endif opt->check(CLI::ExistingPath); } else if (ice::has_all(definition.flags, PF::ValidateFile)) { +#if ISP_LINUX + opt->transform(f); +#endif opt->check(CLI::ExistingFile); } else if (ice::has_all(definition.flags, PF::ValidateDirectory)) { +#if ISP_LINUX + opt->transform(f); +#endif opt->check(CLI::ExistingDirectory); } return opt; diff --git a/source/code/core/utils/public/ice/assert.hxx b/source/code/core/utils/public/ice/assert.hxx index a1b32ef11..58e68b85b 100644 --- a/source/code/core/utils/public/ice/assert.hxx +++ b/source/code/core/utils/public/ice/assert.hxx @@ -35,7 +35,7 @@ namespace ice::detail ice::detail::assert( \ #condition, \ message, \ - fmt::make_format_args(__VA_ARGS__), \ + ice::detail::log_make_args(__VA_ARGS__), \ ice::detail::LogLocation{ .file = __FILE__, .line = __LINE__ } \ ); \ ice::detail::terminate(); \ diff --git a/source/code/core/utils/public/ice/log.hxx b/source/code/core/utils/public/ice/log.hxx index 4d64656af..0071d6a83 100644 --- a/source/code/core/utils/public/ice/log.hxx +++ b/source/code/core/utils/public/ice/log.hxx @@ -25,6 +25,17 @@ namespace ice::detail ice::detail::LogLocation location ) noexcept; + constexpr auto log_make_args() noexcept + { + return fmt::make_format_args(); + } + + template + constexpr auto log_make_args(Args&&... args) noexcept + { + return fmt::make_format_args(args...); + } + } // namespace ice::detail #if defined ICE_LOG @@ -39,7 +50,7 @@ namespace ice::detail severity, \ ice::detail::get_tag(tag), \ format, \ - fmt::make_format_args(__VA_ARGS__), \ + ice::detail::log_make_args(__VA_ARGS__), \ ice::detail::LogLocation{ .file = __FILE__, .line = __LINE__ } \ ); \ } \ @@ -55,7 +66,7 @@ namespace ice::detail severity, \ ice::detail::get_tag(tag), \ format, \ - fmt::make_format_args(__VA_ARGS__), \ + ice::detail::log_make_args(__VA_ARGS__), \ ice::detail::LogLocation{ .file = __FILE__, .line = __LINE__ } \ ); \ } \ diff --git a/source/code/core/utils/public/ice/log_formatters.hxx b/source/code/core/utils/public/ice/log_formatters.hxx index 1518c47fe..032c1da6a 100644 --- a/source/code/core/utils/public/ice/log_formatters.hxx +++ b/source/code/core/utils/public/ice/log_formatters.hxx @@ -12,7 +12,7 @@ template struct fmt::formatter> : public fmt::formatter> { template - constexpr auto format(ice::BasicString value, FormatContext& ctx) + constexpr auto format(ice::BasicString value, FormatContext& ctx) const noexcept { return fmt::formatter>::format({ value._data, value._size }, ctx); } @@ -22,7 +22,7 @@ template struct fmt::formatter> : public fmt::formatter> { template - constexpr auto format(ice::HeapString const& value, FormatContext& ctx) + constexpr auto format(ice::HeapString const& value, FormatContext& ctx) const noexcept { return fmt::formatter>::format({ value._data, value._size }, ctx); } @@ -38,7 +38,7 @@ struct fmt::formatter } template - constexpr auto format(ice::StringID_Hash value, FormatContext& ctx) + constexpr auto format(ice::StringID_Hash value, FormatContext& ctx) const noexcept { if (value.value == ice::stringid_hash(ice::StringID_Invalid).value) { @@ -61,7 +61,7 @@ struct fmt::formatter> } template - constexpr auto format(ice::StringID_Arg value, FormatContext& ctx) + constexpr auto format(ice::StringID_Arg value, FormatContext& ctx) const noexcept { if (value == ice::StringID_Invalid) { @@ -101,7 +101,7 @@ struct fmt::formatter } template - constexpr auto format(ice::usize value, FormatContext& ctx) + constexpr auto format(ice::usize value, FormatContext& ctx) const noexcept { using namespace ice; @@ -170,7 +170,7 @@ struct fmt::formatter } template - constexpr auto format(ice::ErrorCode value, FormatContext& ctx) + constexpr auto format(ice::ErrorCode value, FormatContext& ctx) const noexcept { fmt::string_view const type = value.type() == 'E' ? "Error" : "Success"; return fmt::format_to(ctx.out(), "{}({}, '{}')", type, value.category(), value.description()); @@ -212,16 +212,17 @@ struct fmt::formatter } template - constexpr auto format(ice::TimeType auto value, FormatContext& ctx) + constexpr auto format(ice::TimeType auto value, FormatContext& ctx) const noexcept { - if (presentation == 'd') + char final_presentation = presentation; + if (final_presentation == 'd') { - presentation = presentation_type(value); + final_presentation = presentation_type(value); } if (floatingp == 0) { - switch(presentation) + switch(final_presentation) { case 's': return fmt::format_to(ctx.out(), "{:.3f}s", ice::Ts(value).value, floatingp); case 'm': return fmt::format_to(ctx.out(), "{}ms", ice::Tms(value).value); @@ -232,7 +233,7 @@ struct fmt::formatter } else { - switch(presentation) + switch(final_presentation) { case 's': return fmt::format_to(ctx.out(), "{:.{}f}s", ice::Ts(value).value, floatingp); case 'm': return fmt::format_to(ctx.out(), "{:.{}f}ms", ice::Tms(value).value * 1.0, floatingp); diff --git a/source/code/core/utils/public/ice/native_file.hxx b/source/code/core/utils/public/ice/native_file.hxx index dde649c5d..c5badeba7 100644 --- a/source/code/core/utils/public/ice/native_file.hxx +++ b/source/code/core/utils/public/ice/native_file.hxx @@ -136,6 +136,10 @@ namespace ice::native_file ice::native_file::FilePath path ) noexcept; + bool is_directory( + ice::native_file::FilePath path + ) noexcept; + void path_from_string( ice::native_file::HeapFilePath& out_filepath, ice::String path_string diff --git a/source/code/core/utils/public/ice/string_utils.hxx b/source/code/core/utils/public/ice/string_utils.hxx index f413740d5..390ad17a1 100644 --- a/source/code/core/utils/public/ice/string_utils.hxx +++ b/source/code/core/utils/public/ice/string_utils.hxx @@ -13,6 +13,25 @@ namespace ice { + namespace string + { + + template + constexpr void push_format( + ice::HeapString& str, + fmt::format_string format, + Args&&... args + ) noexcept; + + template + constexpr auto for_each_split( + ice::String contents, + ice::String separators, + Fn&& fn + ) noexcept -> ice::u32; + + } // namespace string + auto utf8_to_wide_size(ice::String path) noexcept -> ice::u32; bool utf8_to_wide_append(ice::String path, ice::HeapString& out_str) noexcept; auto utf8_to_wide(ice::Allocator& alloc, ice::String path) noexcept -> ice::HeapString; @@ -133,6 +152,24 @@ namespace ice str._data[str._size] = '\0'; } + template + constexpr auto for_each_split(ice::String contents, ice::String separator, Fn&& fn) noexcept -> ice::u32 + { + ice::u32 count = 0; + while(ice::string::any(contents)) + { + count += 1; + ice::ucount const separator_pos = ice::string::find_first_of(contents, separator); + ice::String const line = ice::string::substr(contents, 0, separator_pos); + if (ice::forward(fn)(line) == false) + { + break; + } + contents = ice::string::substr(contents, separator_pos + 1); + } + return count; + } + } // namespace string } // namespace ice diff --git a/source/code/example/android/simple/simple.bff b/source/code/example/android/simple/simple.bff index b35e2fe0c..465f43d37 100644 --- a/source/code/example/android/simple/simple.bff +++ b/source/code/example/android/simple/simple.bff @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT .Project = diff --git a/source/code/framework/framework_base/framework_base.bff b/source/code/framework/framework_base/framework_base.bff index 20b56181b..6495f79ae 100644 --- a/source/code/framework/framework_base/framework_base.bff +++ b/source/code/framework/framework_base/framework_base.bff @@ -34,25 +34,27 @@ } ] - .Rule_IsolatedFrameworkObject = + .Rule_Win32 = [ - .Private = + .Public = [ - .UnityInputIsolatedFiles = 'private/framework.cxx' + .Uses = { + 'platform_win32' + } ] ] - .Rule_Win32 = + .Rule_Linux = [ .Public = [ .Uses = { - 'platform_win32' + 'platform_linux' } ] ] .Rules = { - .Rule_IsolatedFrameworkObject .Rule_Win32 + .Rule_Linux } ] .Projects + .Project diff --git a/source/code/framework/framework_base/private/framework_main.cxx b/source/code/framework/framework_base/private/framework_main.cxx index 800692860..41f799647 100644 --- a/source/code/framework/framework_base/private/framework_main.cxx +++ b/source/code/framework/framework_base/private/framework_main.cxx @@ -326,14 +326,14 @@ auto ice_setup( ice::String dylib_path; ice::Array resource_paths{ alloc }; - if constexpr (ice::build::is_release == false && ice::build::is_windows) + if constexpr (ice::build::is_release == false && (ice::build::is_windows || ice::build::is_linux)) { dylib_path = ice::path::directory(ice::app::directory()); config.dev_dirs.shaders = ice::app::workingdir(); config.dev_dirs.assets = ice::app::workingdir(); // Assumes the apps working-dir is in 'build' and no changes where done to shader compilation step - ice::path::join(config.dev_dirs.shaders, "/obj/VkShaders/GFX-Vulkan-Unoptimized-vk-glslc-1-3/data"); + ice::path::join(config.dev_dirs.shaders, "obj/VkShaders/GFX-Vulkan-Unoptimized-vk-glslc-1-3/data"); ice::path::join(config.dev_dirs.assets, "../source/data"); ice::path::normalize(config.dev_dirs.shaders); ice::path::normalize(config.dev_dirs.assets); diff --git a/source/code/iceshard/engine/private/engine_module.cxx b/source/code/iceshard/engine/private/engine_module.cxx index b1e49a4a7..178f67fa0 100644 --- a/source/code/iceshard/engine/private/engine_module.cxx +++ b/source/code/iceshard/engine/private/engine_module.cxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #include diff --git a/source/code/iceshard/engine/private/engine_state_tracker_default.hxx b/source/code/iceshard/engine/private/engine_state_tracker_default.hxx index 8315c7102..0f19d6cb4 100644 --- a/source/code/iceshard/engine/private/engine_state_tracker_default.hxx +++ b/source/code/iceshard/engine/private/engine_state_tracker_default.hxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/iceshard/engine/private/gfx/gfx_utils.cxx b/source/code/iceshard/engine/private/gfx/gfx_utils.cxx index c0e0df837..d4fd2b43e 100644 --- a/source/code/iceshard/engine/private/gfx/gfx_utils.cxx +++ b/source/code/iceshard/engine/private/gfx/gfx_utils.cxx @@ -10,17 +10,6 @@ namespace ice::gfx { - //auto await_shader_program_on( - // ice::String name, - // ice::AssetStorage& assets, - // ice::TaskScheduler& scheduler - //) noexcept -> ice::TaskExpected - //{ - // ice::Expected result = co_await await_shader_program(name, assets); - // co_await scheduler; - // co_return result; - //} - auto load_shader_program( ice::String name, ice::AssetStorage& assets diff --git a/source/code/iceshard/engine/private/gfx/ice_gfx_graph.cxx b/source/code/iceshard/engine/private/gfx/ice_gfx_graph.cxx index d7d76dac4..e0d2dfc5f 100644 --- a/source/code/iceshard/engine/private/gfx/ice_gfx_graph.cxx +++ b/source/code/iceshard/engine/private/gfx/ice_gfx_graph.cxx @@ -214,6 +214,11 @@ namespace ice::gfx IceshardGfxGraphRuntime::~IceshardGfxGraphRuntime() noexcept { + ICE_ASSERT( + this->ready(), + "Graphics Graph destroyed before all tasks finished executing! Possible read/write memory access errors!" + ); + render::RenderDevice& device = _context.device(); for (IceshardGfxGraphStages::Entry* entry : _stages._stages) @@ -233,6 +238,11 @@ namespace ice::gfx device.destroy_renderpass(_renderpass); } + bool IceshardGfxGraphRuntime::ready() const noexcept + { + return _stages._ready.load(std::memory_order_relaxed) == 0; + } + auto initialize_stage(ice::Task<> init_task, std::atomic_uint32_t& ready_count) noexcept -> ice::Task<> { co_await init_task; diff --git a/source/code/iceshard/engine/private/gfx/ice_gfx_graph_runtime.cxx b/source/code/iceshard/engine/private/gfx/ice_gfx_graph_runtime.cxx index b67960cd2..9d9e4187d 100644 --- a/source/code/iceshard/engine/private/gfx/ice_gfx_graph_runtime.cxx +++ b/source/code/iceshard/engine/private/gfx/ice_gfx_graph_runtime.cxx @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT #include "ice_gfx_graph_runtime.hxx" diff --git a/source/code/iceshard/engine/private/gfx/ice_gfx_graph_runtime.hxx b/source/code/iceshard/engine/private/gfx/ice_gfx_graph_runtime.hxx index b7ed77d1c..0874f179c 100644 --- a/source/code/iceshard/engine/private/gfx/ice_gfx_graph_runtime.hxx +++ b/source/code/iceshard/engine/private/gfx/ice_gfx_graph_runtime.hxx @@ -80,6 +80,8 @@ namespace ice::gfx ) noexcept; ~IceshardGfxGraphRuntime() noexcept override; + bool ready() const noexcept override; + bool prepare( ice::gfx::GfxFrameStages& stages, ice::gfx::GfxStageRegistry const& stage_registry, diff --git a/source/code/iceshard/engine/private/gfx/ice_gfx_graph_snapshot.hxx b/source/code/iceshard/engine/private/gfx/ice_gfx_graph_snapshot.hxx index 6cd86ea3c..9833fcac6 100644 --- a/source/code/iceshard/engine/private/gfx/ice_gfx_graph_snapshot.hxx +++ b/source/code/iceshard/engine/private/gfx/ice_gfx_graph_snapshot.hxx @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_concepts.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_concepts.hxx index 16cbe5b3d..317c5069a 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_concepts.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_concepts.hxx @@ -1,3 +1,6 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + #pragma once #include diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_query_storage_entry.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_query_storage_entry.hxx index 038d97ed4..83b3be1bc 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_query_storage_entry.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_query_storage_entry.hxx @@ -4,6 +4,7 @@ #pragma once #include #include +#include namespace ice::ecs { diff --git a/source/code/iceshard/engine/public/ice/engine_module.hxx b/source/code/iceshard/engine/public/ice/engine_module.hxx index b859d4485..8214ac4e7 100644 --- a/source/code/iceshard/engine/public/ice/engine_module.hxx +++ b/source/code/iceshard/engine/public/ice/engine_module.hxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/iceshard/engine/public/ice/engine_state.hxx b/source/code/iceshard/engine/public/ice/engine_state.hxx index 0875e6ee9..8907edb60 100644 --- a/source/code/iceshard/engine/public/ice/engine_state.hxx +++ b/source/code/iceshard/engine/public/ice/engine_state.hxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/iceshard/engine/public/ice/gfx/gfx_graph_resource.hxx b/source/code/iceshard/engine/public/ice/gfx/gfx_graph_resource.hxx index 478605247..7fead2e98 100644 --- a/source/code/iceshard/engine/public/ice/gfx/gfx_graph_resource.hxx +++ b/source/code/iceshard/engine/public/ice/gfx/gfx_graph_resource.hxx @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/iceshard/engine/public/ice/gfx/gfx_graph_runtime.hxx b/source/code/iceshard/engine/public/ice/gfx/gfx_graph_runtime.hxx index 4dcb2e18e..3b8983e65 100644 --- a/source/code/iceshard/engine/public/ice/gfx/gfx_graph_runtime.hxx +++ b/source/code/iceshard/engine/public/ice/gfx/gfx_graph_runtime.hxx @@ -17,6 +17,9 @@ namespace ice::gfx { virtual ~GfxGraphRuntime() noexcept = default; + //! \brief Checks if the graph is ready and all preparation tasks finished executing. + virtual bool ready() const noexcept = 0; + //! \brief Prepares the runtime for the next execution phase. //! \details The runtime will check and prepare the graph for execution if all necessary stages are available and everything was initialized. //! In the case where some stages are still not available, the execute will be skipped. diff --git a/source/code/iceshard/engine/public/ice/gfx/gfx_object.hxx b/source/code/iceshard/engine/public/ice/gfx/gfx_object.hxx index cd48688ed..a0f6acf55 100644 --- a/source/code/iceshard/engine/public/ice/gfx/gfx_object.hxx +++ b/source/code/iceshard/engine/public/ice/gfx/gfx_object.hxx @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/iceshard/engine/public/ice/world/world_trait_module.hxx b/source/code/iceshard/engine/public/ice/world/world_trait_module.hxx index db107eb93..3d494f7d2 100644 --- a/source/code/iceshard/engine/public/ice/world/world_trait_module.hxx +++ b/source/code/iceshard/engine/public/ice/world/world_trait_module.hxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/iceshard/iceshard/private/iceshard_gfx_runner.cxx b/source/code/iceshard/iceshard/private/iceshard_gfx_runner.cxx index d98363256..d224b2eed 100644 --- a/source/code/iceshard/iceshard/private/iceshard_gfx_runner.cxx +++ b/source/code/iceshard/iceshard/private/iceshard_gfx_runner.cxx @@ -132,7 +132,15 @@ namespace ice::gfx } } - _rendergraph = ice::move(rendergraph); + // If we are ready we can set the rendergraph immediately, if not we schedule it for later to be updated. + if (_rendergraph == nullptr || _rendergraph->ready()) + { + _rendergraph = ice::move(rendergraph); + } + else + { + _scheduled_rendergraph = ice::move(rendergraph); + } } auto IceshardGfxRunner::update_data( @@ -209,7 +217,13 @@ namespace ice::gfx ice::execute_tasks(tasks); } - if (_rendergraph->prepare(gpu_stages, *_stages, _gfx_tasks)) + // Check if we have a scheduled render graph and if we can replace + if (_scheduled_rendergraph != nullptr && _rendergraph->ready()) + { + _rendergraph = ice::move(_scheduled_rendergraph); + } + + if (_scheduled_rendergraph == nullptr && _rendergraph->prepare(gpu_stages, *_stages, _gfx_tasks)) { IPT_ZONE_SCOPED_NAMED("gfx_resume_prepare_tasks"); // The tasks here might take more than a frame to finish. We should track them somewhere else. @@ -255,7 +269,7 @@ namespace ice::gfx // Execute all stages (currently done simply going over the render graph) _present_fence->reset(); - if (_rendergraph->execute(frame, *_present_fence)) + if (_scheduled_rendergraph == nullptr && _rendergraph->execute(frame, *_present_fence)) { _rendergraph->present(); } diff --git a/source/code/iceshard/iceshard/private/iceshard_gfx_runner.hxx b/source/code/iceshard/iceshard/private/iceshard_gfx_runner.hxx index 59aa44704..307a39a23 100644 --- a/source/code/iceshard/iceshard/private/iceshard_gfx_runner.hxx +++ b/source/code/iceshard/iceshard/private/iceshard_gfx_runner.hxx @@ -88,6 +88,7 @@ namespace ice::gfx ice::gfx::IceshardGfxRunnerState _state; ice::HashMap _world_states; ice::UniquePtr _rendergraph; + ice::UniquePtr _scheduled_rendergraph; ice::ScopedTaskContainer _gfx_tasks; diff --git a/source/code/modules/imgui_module/private/widgets/imgui_logger.cxx b/source/code/modules/imgui_module/private/widgets/imgui_logger.cxx index 0354a5766..1c9c9e1e3 100644 --- a/source/code/modules/imgui_module/private/widgets/imgui_logger.cxx +++ b/source/code/modules/imgui_module/private/widgets/imgui_logger.cxx @@ -15,6 +15,7 @@ #include #include +#include namespace ice::devui { @@ -73,8 +74,11 @@ namespace ice::devui { } + static std::mutex mtx; // TODO: Might want to replace the mutex with something else + void ImGuiLogger::add_entry(ice::LogSinkMessage const& message) noexcept { + std::lock_guard lk{ mtx }; ice::array::push_back(_entries_visible, ice::array::count(_entries)); ice::array::push_back(_entries, { message.severity, message.tag, message.tag_name, {alloc,message.message} }); } @@ -203,6 +207,8 @@ namespace ice::devui ); } + std::lock_guard lk{ mtx }; + ice::f64 const max_match = _entries[_entries_visible[0]].filter_match; ice::sort_indices( ice::array::slice(_entries), diff --git a/source/code/modules/shader_tools/private/shader_tools.cxx b/source/code/modules/shader_tools/private/shader_tools.cxx index 4cea3d905..67629fa54 100644 --- a/source/code/modules/shader_tools/private/shader_tools.cxx +++ b/source/code/modules/shader_tools/private/shader_tools.cxx @@ -16,7 +16,7 @@ #include "shader_tools_glsl.hxx" #include "shader_tools_wgsl.hxx" -#if ISP_WEBAPP || ISP_ANDROID +#if ISP_UNIX #define strcmpi strcasecmp #endif @@ -60,6 +60,11 @@ namespace ice 2u - ice::u32(target == ShaderTargetPlatform::WGSL) ); } + else if constexpr (ice::build::is_linux) + { + static ice::String supported_extensions[]{ ".asl", ".glsl" }; + return supported_extensions; + } else if constexpr (ice::build::is_webapp) { static ice::String supported_extensions[]{ ".asl", ".wgsl" }; @@ -80,7 +85,7 @@ namespace ice ice::Allocator& alloc ) noexcept -> ice::Task { -#if ISP_WINDOWS +#if ISP_WINDOWS || ISP_LINUX ShaderCompilerContext& ctx = *shader_context(resctx); if (ctx.target == ShaderTargetPlatform::GLSL) { @@ -93,6 +98,9 @@ namespace ice #elif ISP_WEBAPP return wgsl::compiler_compile_shader_source(resctx, source, tracker, resources, uris, alloc); #elif ISP_ANDROID + co_return {}; // Empty is expected +#else + ICE_ASSERT(false, "Missing implementation!"); co_return {}; #endif } @@ -117,7 +125,7 @@ namespace ice auto compiler_result_extension(ice::Span params) noexcept -> ice::String { -#if ISP_WINDOWS +#if ISP_WINDOWS || ISP_LINUX ice::ShaderTargetPlatform const target = param_shader_target(params); switch (target) { @@ -135,7 +143,7 @@ namespace ice { static void v1_compiler_api(ice::api::resource_compiler::v1::ResourceCompilerAPI& api) noexcept { -#if ISP_WINDOWS || ISP_WEBAPP +#if ISP_WINDOWS || ISP_LINUX || ISP_WEBAPP api.fn_prepare_context = compiler_context_prepare; api.fn_cleanup_context = compiler_context_cleanup; api.fn_supported_resources = compiler_supported_shader_resources; @@ -260,6 +268,6 @@ namespace ice } // namespace ice -#if ISP_WEBAPP || ISP_ANDROID +#if ISP_UNIX #undef strcmpi #endif diff --git a/source/code/modules/shader_tools/private/shader_tools_glsl.cxx b/source/code/modules/shader_tools/private/shader_tools_glsl.cxx index 6d16359c2..ecf4d2f2f 100644 --- a/source/code/modules/shader_tools/private/shader_tools_glsl.cxx +++ b/source/code/modules/shader_tools/private/shader_tools_glsl.cxx @@ -3,7 +3,8 @@ #include "shader_tools_glsl.hxx" -#if ISP_WINDOWS +#if ISP_WINDOWS || ISP_LINUX + #include #include #include diff --git a/source/code/modules/shader_tools/private/shader_tools_glsl.hxx b/source/code/modules/shader_tools/private/shader_tools_glsl.hxx index 4806f1cda..a1e6edf06 100644 --- a/source/code/modules/shader_tools/private/shader_tools_glsl.hxx +++ b/source/code/modules/shader_tools/private/shader_tools_glsl.hxx @@ -6,7 +6,8 @@ #include #include -#if ISP_WINDOWS +#if ISP_WINDOWS || ISP_LINUX + #include #include "shader_tools_asl.hxx" diff --git a/source/code/modules/shader_tools/private/shader_tools_wgsl.cxx b/source/code/modules/shader_tools/private/shader_tools_wgsl.cxx index 48199a59e..10c1c67c1 100644 --- a/source/code/modules/shader_tools/private/shader_tools_wgsl.cxx +++ b/source/code/modules/shader_tools/private/shader_tools_wgsl.cxx @@ -3,7 +3,7 @@ #include "shader_tools_wgsl.hxx" -#if ISP_WEBAPP || ISP_WINDOWS +#if ISP_WINDOWS || ISP_LINUX || ISP_WEBAPP #include #include #include diff --git a/source/code/modules/shader_tools/private/shader_tools_wgsl.hxx b/source/code/modules/shader_tools/private/shader_tools_wgsl.hxx index 4876f5e48..9334d27ca 100644 --- a/source/code/modules/shader_tools/private/shader_tools_wgsl.hxx +++ b/source/code/modules/shader_tools/private/shader_tools_wgsl.hxx @@ -5,7 +5,7 @@ #include #include -#if ISP_WEBAPP || ISP_WINDOWS +#if ISP_WINDOWS || ISP_LINUX || ISP_WEBAPP #include #include "shader_tools_asl.hxx" diff --git a/source/code/modules/shader_tools/shader_tools.bff b/source/code/modules/shader_tools/shader_tools.bff index 7add39958..bb6d66328 100644 --- a/source/code/modules/shader_tools/shader_tools.bff +++ b/source/code/modules/shader_tools/shader_tools.bff @@ -8,6 +8,7 @@ .Group = 'Modules' .BaseDir = '$WorkspaceCodeDir$/modules/shader_tools' + .CopyModules = true .LocalCompiler = [ @@ -29,7 +30,8 @@ .SDK_VulkanShaderC = [ .Name = 'Vulkan-Shader-Compiler' - .Requires = { 'SDK-Vulkan', 'SDK-Windows-10' } + .Requires = { 'SDK-Vulkan' } + .RequiresAny = { 'Windows', 'Linux' } .Public = [ diff --git a/source/code/modules/vulkan_renderer/private/vk_allocator.cxx b/source/code/modules/vulkan_renderer/private/vk_allocator.cxx index 45a708619..e43e1a6bd 100644 --- a/source/code/modules/vulkan_renderer/private/vk_allocator.cxx +++ b/source/code/modules/vulkan_renderer/private/vk_allocator.cxx @@ -60,16 +60,18 @@ namespace ice::render::vk auto vk_iceshard_allocate(void* userdata, size_t size, size_t alignment, VkSystemAllocationScope /*scope*/) noexcept -> void* { - if (size == 0) return nullptr; + // if (size == 0) return nullptr; VulkanAllocator* const allocator = reinterpret_cast(userdata); - return allocator->allocate({ { size }, static_cast(alignment) }).memory; + AllocResult const res = allocator->allocate({ { size }, static_cast(alignment) }); + return res.memory; } auto vk_iceshard_reallocate(void* userdata, void* original, size_t size, size_t alignment, VkSystemAllocationScope /*scope*/) noexcept -> void* { VulkanAllocator* const allocator = reinterpret_cast(userdata); - return allocator->do_reallocate(original, { { size }, static_cast(alignment) }).memory; + AllocResult const res = allocator->do_reallocate(original, { { size }, static_cast(alignment) }); + return res.memory; } void vk_iceshard_free(void* userdata, void* pointer) noexcept @@ -111,6 +113,10 @@ namespace ice::render::vk auto VulkanAllocator::do_allocate(ice::AllocRequest request) noexcept -> ice::AllocResult { +#if 0 + request.alignment = ice::max(request.alignment, ice::ualign::b_4); + ICE_ASSERT_CORE(request.alignment >= ice::ualign::b_4); + ice::meminfo subreq = ice::meminfo_of; ice::usize const offset_data = subreq += ice::meminfo{ request.size, request.alignment }; @@ -128,11 +134,25 @@ namespace ice::render::vk .size = request.size, .alignment = request.alignment }; +#else + ICE_ASSERT_CORE(request.alignment <= ice::ualign::b_8); + request.alignment = ice::ualign::b_8; + request.size = ice::align_to(request.size, request.alignment).value + 8_B; + + ice::AllocResult result = _backing_allocator.allocate(request); + ice::u32* header = reinterpret_cast(result.memory); + result.memory = ice::ptr_add(result.memory, 8_B); + result.size.value -= 8; + header[0] = ice::u32(result.size.value); + header[1] = ice::u32(request.alignment); + return result; +#endif } //! \brief #todo auto VulkanAllocator::do_reallocate(void* pointer, ice::AllocRequest req) noexcept -> ice::AllocResult { +#if 0 // We will return a nullptr when deallocating, as there was not indication what to return during deallocations. ice::AllocResult result{ }; if (req.size == 0_B) @@ -165,13 +185,50 @@ namespace ice::render::vk } } return result; +#else + // We will return a nullptr when deallocating, as there was not indication what to return during deallocations. + ice::AllocResult result{ }; + if (req.size == 0_B) + { + this->deallocate(pointer); + } + else + { + if (pointer == nullptr) + { + result = this->allocate(req); + } + else // original != nullptr + { + ice::u32 const* header = reinterpret_cast(ice::ptr_sub(pointer, 8_B)); + ice::usize const size{ header[0] }; + ice::ualign const align{ ice::ualign(header[1]) }; + + ICE_ASSERT_CORE(req.alignment <= align); + + ice::AllocResult new_res = this->allocate({ req.size, align }); + ICE_ASSERT_CORE(new_res.memory != nullptr); + ice::memcpy(new_res.memory, pointer, ice::min(ice::usize{ size }, req.size)); + + this->deallocate(pointer); + result = new_res; + } + } + return result; +#endif } //! \copydoc allocator::deallocate(void*) noexcept void VulkanAllocator::do_deallocate(void* pointer) noexcept { +#if 0 // We need to pass the header pointer because this pointer was returned by the backing allocator. _backing_allocator.deallocate(detail::header(pointer)); +#else + ice::u32 const* header = reinterpret_cast(ice::ptr_sub(pointer, 8_B)); + + _backing_allocator.deallocate((void*)header); +#endif } auto VulkanAllocator::vulkan_callbacks() const noexcept -> VkAllocationCallbacks const* diff --git a/source/code/modules/vulkan_renderer/private/vk_buffer.hxx b/source/code/modules/vulkan_renderer/private/vk_buffer.hxx index a17e8379d..7d3d24254 100644 --- a/source/code/modules/vulkan_renderer/private/vk_buffer.hxx +++ b/source/code/modules/vulkan_renderer/private/vk_buffer.hxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/modules/vulkan_renderer/private/vk_device.cxx b/source/code/modules/vulkan_renderer/private/vk_device.cxx index 07a63be39..eb1534494 100644 --- a/source/code/modules/vulkan_renderer/private/vk_device.cxx +++ b/source/code/modules/vulkan_renderer/private/vk_device.cxx @@ -118,18 +118,29 @@ namespace ice::render::vk "Failed to query surface capabilities!" ); - VkSurfaceFormatKHR surface_format = ice::array::front(surface_formats); + VkSurfaceFormatKHR selected_format = ice::array::front(surface_formats); + if constexpr (ice::build::is_linux) + { + // If possible select UNORM istead of SRGB + for (VkSurfaceFormatKHR format : surface_formats) + { + if (format.format == VK_FORMAT_B8G8R8A8_UNORM) + { + selected_format = format; + } + } + } VkSwapchainCreateInfoKHR swapchain_info{ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; swapchain_info.surface = vk_surface->handle(); - swapchain_info.imageFormat = surface_format.format; + swapchain_info.imageFormat = selected_format.format; swapchain_info.minImageCount = ice::max(surface_capabilities.minImageCount, 2u); swapchain_info.imageExtent = surface_capabilities.currentExtent; swapchain_info.imageArrayLayers = 1; swapchain_info.oldSwapchain = VK_NULL_HANDLE; swapchain_info.clipped = false; // Clipped for android only? swapchain_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; - swapchain_info.imageColorSpace = surface_format.colorSpace; + swapchain_info.imageColorSpace = selected_format.colorSpace; swapchain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swapchain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchain_info.queueFamilyIndexCount = 0; @@ -187,7 +198,7 @@ namespace ice::render::vk return _allocator.create( vk_swapchain, - surface_format.format, + selected_format.format, swapchain_info.imageExtent, _vk_device ); diff --git a/source/code/modules/vulkan_renderer/private/vk_driver.cxx b/source/code/modules/vulkan_renderer/private/vk_driver.cxx index 5d7d4091f..80c71f037 100644 --- a/source/code/modules/vulkan_renderer/private/vk_driver.cxx +++ b/source/code/modules/vulkan_renderer/private/vk_driver.cxx @@ -10,6 +10,12 @@ #include #include +extern "C" +{ + struct wl_display; + struct wl_surface; +} + namespace ice::render::vk { @@ -71,6 +77,11 @@ namespace ice::render::vk { _vk_physical_device = physical_device; } + if (physical_device_properties.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU + && _vk_physical_device == vk_nullptr) + { + _vk_physical_device = physical_device; + } } } @@ -211,6 +222,74 @@ namespace ice::render::vk return _vk_alloc->create(_vk_instance, vulkan_surface); } +#elif ISP_LINUX + auto VulkanRenderDriver::create_surface( + ice::render::SurfaceInfo const& surface_info + ) noexcept -> ice::render::RenderSurface* + { + ICE_ASSERT( + surface_info.type == ice::render::SurfaceType::Wayland_Window + || surface_info.type == ice::render::SurfaceType::X11_Window, + "Unsupported surface type provided, accepting 'Wayland_Window' or 'X11_Window' surfaces only!" + ); + + VkSurfaceKHR vulkan_surface; + if (surface_info.type == ice::render::SurfaceType::Wayland_Window) + { + VkWaylandSurfaceCreateInfoKHR surface_create_info{ VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR }; + surface_create_info.display = static_cast(surface_info.wayland.display); + surface_create_info.surface = static_cast(surface_info.wayland.surface); + + auto api_result = vkCreateWaylandSurfaceKHR(_vk_instance, &surface_create_info, nullptr, &vulkan_surface); + ICE_ASSERT(api_result == VkResult::VK_SUCCESS, "Failed to create Vulkan surface!"); + } + else + { + VkXlibSurfaceCreateInfoKHR surface_create_info{ VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR }; + surface_create_info.dpy = static_cast(surface_info.x11.display); + surface_create_info.window = surface_info.x11.window; + + auto api_result = vkCreateXlibSurfaceKHR(_vk_instance, &surface_create_info, nullptr, &vulkan_surface); + ICE_ASSERT(api_result == VkResult::VK_SUCCESS, "Failed to create Vulkan surface!"); + } + + ice::i32 family_index = 0; + for (VkQueueFamilyProperties const& queue_family_props : _vk_queue_family_properties) + { + VkBool32 supports_presenting; + + [[maybe_unused]] + VkResult ph_api_result = vkGetPhysicalDeviceSurfaceSupportKHR( + _vk_physical_device, + family_index, + vulkan_surface, + &supports_presenting + ); + ICE_ASSERT( + ph_api_result == VkResult::VK_SUCCESS, + "Couldn't query information if family {} (index) supports presenting!", + family_index + ); + + if (supports_presenting == VK_TRUE) + { + if (_vk_presentation_queue_family_index == -1) + { + _vk_presentation_queue_family_index = family_index; + } + else if ((queue_family_props.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) + { + _vk_presentation_queue_family_index = family_index; + } + } + + family_index += 1; + } + + return _vk_alloc->create(_vk_instance, vulkan_surface); + } +#else +#error Missing implementation #endif void VulkanRenderDriver::destroy_surface( diff --git a/source/code/modules/vulkan_renderer/private/vk_extensions.cxx b/source/code/modules/vulkan_renderer/private/vk_extensions.cxx index dfbb42c9d..3fb0b6b40 100644 --- a/source/code/modules/vulkan_renderer/private/vk_extensions.cxx +++ b/source/code/modules/vulkan_renderer/private/vk_extensions.cxx @@ -14,6 +14,9 @@ namespace ice::render::vk { ExtensionTarget::InstanceExtension, Extension::VkI_Win32Surface, 0, VK_KHR_WIN32_SURFACE_EXTENSION_NAME }, #elif ISP_ANDROID { ExtensionTarget::InstanceExtension, Extension::VkI_AndroidSurface, 0, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME }, +#elif ISP_LINUX + { ExtensionTarget::InstanceExtension, Extension::VkI_WaylandSurface, 0, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME }, + { ExtensionTarget::InstanceExtension, Extension::VkI_XLibSurface, 0, VK_KHR_XLIB_SURFACE_EXTENSION_NAME }, #else #error Unknown platform #endif diff --git a/source/code/modules/vulkan_renderer/private/vk_extensions.hxx b/source/code/modules/vulkan_renderer/private/vk_extensions.hxx index f75c2f02c..ae267243b 100644 --- a/source/code/modules/vulkan_renderer/private/vk_extensions.hxx +++ b/source/code/modules/vulkan_renderer/private/vk_extensions.hxx @@ -14,8 +14,10 @@ namespace ice::render::vk // Core instance extensions VkI_Surface = 0x0000'0001, - VkI_Win32Surface= 0x0000'0002, - VkI_AndroidSurface= 0x0000'0004, + VkI_Win32Surface = 0x0000'0002, // The three surfaces are mutually exclusive so they may share the same value + VkI_AndroidSurface = 0x0000'0002, + VkI_WaylandSurface = 0x0000'0002, + VkI_XLibSurface = 0x0000'0004, VkI_GetPhysicalDeviceProperties2 = 0x0000'0008, // Core device extensions diff --git a/source/code/modules/vulkan_renderer/private/vk_fence.cxx b/source/code/modules/vulkan_renderer/private/vk_fence.cxx index 7799a79af..437fb4f76 100644 --- a/source/code/modules/vulkan_renderer/private/vk_fence.cxx +++ b/source/code/modules/vulkan_renderer/private/vk_fence.cxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #include "vk_fence.hxx" diff --git a/source/code/modules/vulkan_renderer/private/vk_image.hxx b/source/code/modules/vulkan_renderer/private/vk_image.hxx index 311e018ed..15547bf43 100644 --- a/source/code/modules/vulkan_renderer/private/vk_image.hxx +++ b/source/code/modules/vulkan_renderer/private/vk_image.hxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/modules/vulkan_renderer/private/vk_include.hxx b/source/code/modules/vulkan_renderer/private/vk_include.hxx index bef2e0946..523c1fae8 100644 --- a/source/code/modules/vulkan_renderer/private/vk_include.hxx +++ b/source/code/modules/vulkan_renderer/private/vk_include.hxx @@ -5,6 +5,10 @@ #include #include +#undef Complex +#undef None +#undef Bool + constexpr nullptr_t vk_nullptr = VK_NULL_HANDLE; // Instance Extension Functions diff --git a/source/code/modules/vulkan_renderer/private/vk_shader_asset.cxx b/source/code/modules/vulkan_renderer/private/vk_shader_asset.cxx index 632b9ec69..2975957e4 100644 --- a/source/code/modules/vulkan_renderer/private/vk_shader_asset.cxx +++ b/source/code/modules/vulkan_renderer/private/vk_shader_asset.cxx @@ -56,7 +56,7 @@ namespace ice::render::vk ice::ModuleQuery const& module_query ) noexcept { -#if ISP_WINDOWS +#if ISP_WINDOWS || ISP_LINUX static ice::String constexpr extensions[]{ ".asl", ".glsl",".spv" }; #else static ice::String constexpr extensions[]{ ".spv" }; @@ -68,7 +68,7 @@ namespace ice::render::vk .fn_asset_loader = asset_shader_loader }; -#if ISP_WINDOWS +#if ISP_WINDOWS || ISP_LINUX ice::ResourceCompiler compiler{ }; module_query.query_api(compiler); asset_category_archive.register_category(ice::render::AssetCategory_Shader, definition, &compiler); diff --git a/source/code/platforms/application/private/app_info.cxx b/source/code/platforms/application/private/app_info.cxx index 4f56fcf64..bea93d8c6 100644 --- a/source/code/platforms/application/private/app_info.cxx +++ b/source/code/platforms/application/private/app_info.cxx @@ -1,7 +1,11 @@ /// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT +#include #include +#include +#include +#include namespace ice::app { @@ -16,4 +20,100 @@ namespace ice::app return { "iceshard-application" }; } +#if ISP_WINDOWS + auto location() noexcept -> ice::String + { + static ice::StaticString<256> app_location = []() noexcept + { + ice::StaticString<256, ice::wchar> location_wide{ L"" }; + DWORD const path_size = GetModuleFileNameW(NULL, ice::string::begin(location_wide), ice::string::capacity(location_wide)); + ice::string::resize(location_wide, path_size); + + ice::StackAllocator_1024 stack_alloc; + ice::HeapString<> location_utf8{ stack_alloc }; + ice::wide_to_utf8_append(location_wide, location_utf8); + + return ice::StaticString<256>{ location_utf8 }; + }(); + + return app_location; + } + + auto directory() noexcept -> ice::String + { + static ice::String app_directory = ice::path::directory(location()); + return app_directory; + } + + auto workingdir() noexcept -> ice::String + { + static ice::StaticString<256> working_dir = []() noexcept + { + ice::StaticString<256, ice::wchar> location_wide{ L"" }; + DWORD const path_size = GetCurrentDirectoryW(ice::string::capacity(location_wide), ice::string::begin(location_wide)); + ice::string::resize(location_wide, path_size); + + ice::StackAllocator_1024 stack_alloc; + ice::HeapString<> location_utf8{ stack_alloc }; + ice::wide_to_utf8_append(location_wide, location_utf8); + + return ice::StaticString<256>{ location_utf8 }; + }(); + + return working_dir; + } +#elif ISP_LINUX + auto location() noexcept -> ice::String + { + static ice::StaticString app_location = []() noexcept + { + ice::StaticString result{ "" }; + int nchar = readlink("/proc/self/exe", ice::string::begin(result), ice::string::capacity(result)); + ice::string::resize(result, nchar); + return result; + }(); + + return app_location; + } + + auto directory() noexcept -> ice::String + { + static ice::String app_directory = ice::path::directory(location()); + return app_directory; + } + + auto workingdir() noexcept -> ice::String + { + static ice::StaticString working_dir = []() noexcept + { + ice::StaticString result{}; + char const* success = getcwd(ice::string::begin(result), ice::string::capacity(result)); + ICE_ASSERT(success != nullptr, "Current working directory is too long, can't contain the value!"); + ice::string::resize(result, std::strlen(success)); + + return ice::StaticString<256>{ result }; + }(); + + return working_dir; + } +#else + auto location() noexcept -> ice::String + { + // TODO: Deprecate or return a valid value + return {}; + } + + auto directory() noexcept -> ice::String + { + // TODO: Deprecate or return a valid value + return {}; + } + + auto workingdir() noexcept -> ice::String + { + // TODO: Deprecate or return a valid value + return {}; + } +#endif + } // namespace ice::app diff --git a/source/code/platforms/platform/public/ice/platform_render_surface.hxx b/source/code/platforms/platform/public/ice/platform_render_surface.hxx index 2063e763f..d6c400d45 100644 --- a/source/code/platforms/platform/public/ice/platform_render_surface.hxx +++ b/source/code/platforms/platform/public/ice/platform_render_surface.hxx @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/platforms/platform_android/private/android_app_info.cxx b/source/code/platforms/platform_android/private/android_app_info.cxx deleted file mode 100644 index b7a87197f..000000000 --- a/source/code/platforms/platform_android/private/android_app_info.cxx +++ /dev/null @@ -1,27 +0,0 @@ -/// Copyright 2023 - 2025, Dandielo -/// SPDX-License-Identifier: MIT - -#include - -namespace ice::app -{ - - auto location() noexcept -> ice::String - { - // TODO: Deprecate or return a valid value - return {}; - } - - auto directory() noexcept -> ice::String - { - // TODO: Deprecate or return a valid value - return {}; - } - - auto workingdir() noexcept -> ice::String - { - // TODO: Deprecate or return a valid value - return {}; - } - -} // namespace ice::app diff --git a/source/code/platforms/platform_android/private/android_main.cxx b/source/code/platforms/platform_android/private/android_main.cxx index 29e5edcd6..1ca1d76c2 100644 --- a/source/code/platforms/platform_android/private/android_main.cxx +++ b/source/code/platforms/platform_android/private/android_main.cxx @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT #include "android_app.hxx" diff --git a/source/code/platforms/platform_android/private/android_render_surface.hxx b/source/code/platforms/platform_android/private/android_render_surface.hxx index 65b0db123..4299795b5 100644 --- a/source/code/platforms/platform_android/private/android_render_surface.hxx +++ b/source/code/platforms/platform_android/private/android_render_surface.hxx @@ -1,4 +1,4 @@ -/// Copyright 2024 - 2024, Dandielo +/// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/platforms/platform_linux/platform_linux.bff b/source/code/platforms/platform_linux/platform_linux.bff new file mode 100644 index 000000000..d23ff5f35 --- /dev/null +++ b/source/code/platforms/platform_linux/platform_linux.bff @@ -0,0 +1,27 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +.Project = +[ + .Name = 'platform_linux' + .Kind = .Kind_ObjectList + .BaseDir = '$WorkspaceCodeDir$/platforms/platform_linux' + .Group = 'Platforms' + + .Requires = { 'SDK-Linux' } + + .Public = + [ + .Uses = { + 'application' + 'platform' + 'utils' + 'tasks' + } + + .Modules = { // Move to private? + 'sdl2' + } + ] +] +.Projects + .Project diff --git a/source/code/platforms/platform_linux/private/linux_main.cxx b/source/code/platforms/platform_linux/private/linux_main.cxx new file mode 100644 index 000000000..2ba8b6ea4 --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_main.cxx @@ -0,0 +1,90 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#include +#include +#include +#include + +int main(int argc, char const** argv) +{ + ice::i32 app_result = 0; + + // The application lifetime scope + { + ice::HostAllocator host_alloc{ }; + + ice::app::Factories app_factories{ }; + ice_init(host_alloc, app_factories); + + ice::Params params = ice::create_params(host_alloc, "iceshard", "0.0.1", "IceShard engine parameters"); + ice::UniquePtr config = app_factories.factory_config(host_alloc); + + ice_args(host_alloc, params, *config); + + ice::UniquePtr state = app_factories.factory_state(host_alloc); + + ice::Result result = ice_setup(host_alloc, *config, *state); + while(result == ice::app::S_ApplicationSetupPending) + { + result = ice_setup(host_alloc, *config, *state); + } + + ICE_LOG_IF(result == false, ice::LogSeverity::Critical, ice::LogTag::Core, "{}\n", result.error().description()); + ICE_ASSERT_CORE(result == true); + + // Before updating we need to resume first. + if (result == ice::app::S_ApplicationUpdate) + { + ICE_LOG(ice::LogSeverity::Warning, ice::LogTag::Core, "An application should always move to 'Resume' after finishing the 'Setup' stage!"); + result = ice::app::S_ApplicationResume; + } + + // We can only call exit if we are in 'suspended' state. + // Since initially we never resumed we start with 'true' + bool suspended_before_exit = true; + + ice::UniquePtr runtime = app_factories.factory_runtime(host_alloc); + while (result && result != ice::app::S_ApplicationExit) + { + // Resume the apllication (always called at least once) + // Allows to update the application state + if (result == ice::app::S_ApplicationResume) + { + suspended_before_exit = false; + result = ice_resume(*config, *state, *runtime); + } + + // Only enter update if resume was successful + while(result == ice::app::S_ApplicationUpdate) + { + result = ice_update(*config, *state, *runtime); + } + + // Suspend the apllication (always called at least once) + // Allows to cleanup the application state + if (result == ice::app::S_ApplicationSuspend) + { + result = ice_suspend(*config, *state, *runtime); + suspended_before_exit = true; + } + } + + // We only suspend if no error was triggered. + if (result && suspended_before_exit == false) + { + result = ice_suspend(*config, *state, *runtime); + } + + runtime.reset(); + + ICE_LOG_IF(result == false, ice::LogSeverity::Error, ice::LogTag::Core, "{}\n", result.error().description()); + ICE_ASSERT_CORE(result == true); + + result = ice_shutdown(host_alloc, *config, *state); + ICE_LOG_IF(result == false, ice::LogSeverity::Error, ice::LogTag::Core, "{}\n", result.error().description()); + ICE_ASSERT_CORE(result == true); + } + + return app_result; +} diff --git a/source/code/platforms/platform_linux/private/linux_sdl2.hxx b/source/code/platforms/platform_linux/private/linux_sdl2.hxx new file mode 100644 index 000000000..473d0c9b8 --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_sdl2.hxx @@ -0,0 +1,9 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#pragma once +#include +#include + +#undef None +#undef main diff --git a/source/code/platforms/platform_linux/private/linux_sdl2_platform.cxx b/source/code/platforms/platform_linux/private/linux_sdl2_platform.cxx new file mode 100644 index 000000000..2e87baff8 --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_sdl2_platform.cxx @@ -0,0 +1,255 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include + +#include "linux_sdl2_platform.hxx" +#include "linux_sdl2_utils.hxx" +#include "linux_storage.hxx" +#include "linux_threads.hxx" + +namespace ice::platform::linux::sdl2 +{ + + Platform_LinuxSDL2::Platform_LinuxSDL2(ice::Allocator& alloc) noexcept + : _alloc{ alloc, "iceshard.platform-layer" } + , _system_events{ _alloc } + , _input_events{ _alloc } + , _render_surface{ } + { + ice::shards::reserve(_system_events, 32); + ice::array::reserve(_input_events._events, 512); + + SDL_InitSubSystem(SDL_INIT_EVENTS); + + using namespace ice::input; + } + + Platform_LinuxSDL2::~Platform_LinuxSDL2() noexcept + { + SDL_QuitSubSystem(SDL_INIT_EVENTS); + SDL_Quit(); + } + + auto Platform_LinuxSDL2::refresh_events() noexcept -> ice::Result + { + IPT_ZONE_SCOPED; + + using namespace ice::input; + + _input_events.clear(); + ice::shards::clear(_system_events); + + static bool first_refresh = true; + if (first_refresh) + { + first_refresh = false; + + _input_events.push( + make_device_handle(DeviceType::Mouse, DeviceIndex{ 0 }), + DeviceMessage::DeviceConnected + ); + _input_events.push( + make_device_handle(DeviceType::Keyboard, DeviceIndex{ 0 }), + DeviceMessage::DeviceConnected + ); + } + + static char text_buffer[32]; + static SDL_Event current_event{ }; + while (SDL_PollEvent(¤t_event) != 0) + { + switch (current_event.type) + { + case SDL_QUIT: + ice::shards::push_back(_system_events, ice::platform::Shard_AppQuit); + + _input_events.push( + make_device_handle(DeviceType::Keyboard, DeviceIndex(0)), + DeviceMessage::DeviceDisconnected + ); + _input_events.push( + make_device_handle(DeviceType::Mouse, DeviceIndex(0)), + DeviceMessage::DeviceDisconnected + ); + break; + case SDL_WINDOWEVENT: + { + ice::vec2i const window_size{current_event.window.data1, current_event.window.data2}; + ICE_LOG(LogSeverity::Debug, LogTag::Core, "Window event ID: {}", current_event.window.event); + + switch (current_event.window.event) + { + case SDL_WINDOWEVENT_MINIMIZED: + ice::shards::push_back(_system_events, { ice::platform::ShardID_WindowMinimized }); + break; + case SDL_WINDOWEVENT_RESTORED: + ice::shards::push_back(_system_events, ice::platform::ShardID_WindowRestored | window_size); + break; + case SDL_WINDOWEVENT_MAXIMIZED: + ice::shards::push_back(_system_events, ice::platform::ShardID_WindowMaximized | window_size); + break; + case SDL_WINDOWEVENT_SIZE_CHANGED: + case SDL_WINDOWEVENT_RESIZED: + ice::shards::push_back(_system_events, ice::platform::ShardID_WindowResized | window_size); + break; + } + } + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEWHEEL: + case SDL_MOUSEMOTION: + mouse_input_events(_input_events, current_event); + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + keyboard_input_events(_input_events, current_event); + break; + // [issue #33] + case SDL_TEXTINPUT: + ice::memcpy(text_buffer, current_event.text.text, 32); + ice::shards::push_back( + _system_events, + ice::platform::ShardID_InputText | (char const*)text_buffer + ); + } + } + + return S_Success; + } + +} // namespace ice::platform::linux::sdl2 + +namespace ice::platform +{ + + static linux::sdl2::Platform_LinuxSDL2* core_feature; + static ice::UniquePtr storage_feature; + static ice::UniquePtr threads_feature; + + auto available_features() noexcept -> ice::platform::FeatureFlags + { + return FeatureFlags::Core + | FeatureFlags::RenderSurface + | FeatureFlags::StoragePaths + | FeatureFlags::Threads; + } + + auto initialize( + ice::platform::FeatureFlags flags, + ice::Span params + ) noexcept -> ice::Result + { + static ice::HostAllocator host_alloc; + return initialize_with_allocator(host_alloc, flags, params); + } + + auto initialize_with_allocator( + ice::Allocator& alloc, + ice::platform::FeatureFlags flags, + ice::Span params + ) noexcept -> ice::Result + { + IPT_ZONE_SCOPED; + + alignas(alignof(linux::sdl2::Platform_LinuxSDL2)) static char instance_buffer[sizeof(linux::sdl2::Platform_LinuxSDL2)]; + + if (core_feature != nullptr) + { + return E_PlatformAlreadyInitialized; + } + + if (flags == FeatureFlags::None || !ice::has_all(available_features(), flags)) + { + return E_InvalidArgument; + } + + if (ice::has_any(flags, FeatureFlags::StoragePaths)) + { + storage_feature = ice::make_unique(alloc, alloc, params); + } + + if (ice::has_any(flags, FeatureFlags::StoragePaths)) + { + threads_feature = ice::make_unique(alloc, alloc, params); + } + + // Initialize the global platform instance. We don't use the allocator so we don't leak this pointer. + core_feature = new (instance_buffer) linux::sdl2::Platform_LinuxSDL2{ alloc }; + return S_Success; + } + + auto query_api(ice::platform::FeatureFlags flag, void*& out_api_ptr) noexcept -> ice::Result + { + linux::sdl2::Platform_LinuxSDL2* instance_ptr = core_feature; + ICE_ASSERT(instance_ptr != nullptr, "Platform not initialized!"); + + if (ice::has_all(available_features(), flag) == false) + { + return E_PlatformFeatureNotAvailable; + } + + switch (flag) + { + case FeatureFlags::Core: + out_api_ptr = static_cast(instance_ptr); + break; + case FeatureFlags::StoragePaths: + out_api_ptr = storage_feature.get(); + break; + case FeatureFlags::Threads: + out_api_ptr = threads_feature.get(); + break; + case FeatureFlags::RenderSurface: + out_api_ptr = static_cast(ice::addressof(instance_ptr->_render_surface)); + break; + default: + return E_InvalidArgument; + } + + return S_Success; + } + + auto query_apis(ice::platform::FeatureFlags flags, void** out_api_ptrs) noexcept -> ice::Result + { + ice::Result result = S_Success; + for (FeatureFlags flag : { FeatureFlags::Core, FeatureFlags::RenderSurface }) + { + if (ice::has_all(flags, flag)) + { + result = query_api(flag, *out_api_ptrs); + if (result == false) + { + break; + } + out_api_ptrs += 1; + } + else + { + result = E_InvalidArgument; + break; + } + } + + return result; + } + + void shutdown() noexcept + { + IPT_ZONE_SCOPED; + + linux::sdl2::Platform_LinuxSDL2*& instance_ptr = core_feature; + if (instance_ptr != nullptr) + { + storage_feature.reset(); + threads_feature.reset(); + instance_ptr->~Platform_LinuxSDL2(); + instance_ptr = nullptr; + } + } + +} // namespace ice::platform diff --git a/source/code/platforms/platform_linux/private/linux_sdl2_platform.hxx b/source/code/platforms/platform_linux/private/linux_sdl2_platform.hxx new file mode 100644 index 000000000..194eccedd --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_sdl2_platform.hxx @@ -0,0 +1,39 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#pragma once +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "linux_sdl2_platform_render_surface.hxx" + +namespace ice::platform::linux::sdl2 +{ + + struct Platform_LinuxSDL2 final : Core + { + Platform_LinuxSDL2(ice::Allocator& alloc) noexcept; + ~Platform_LinuxSDL2() noexcept; + + auto refresh_events() noexcept -> ice::Result override; + + auto system_events() noexcept -> ice::ShardContainer const& override { return _system_events; } + auto input_events() noexcept -> ice::Span override { return ice::array::slice(_input_events._events); } + + auto allocator() noexcept -> ice::Allocator& { return _alloc.backing_allocator(); } + + ice::ProxyAllocator _alloc; + ice::ShardContainer _system_events; + ice::input::DeviceEventQueue _input_events; + + RenderSurface_WaylandX11SDL2 _render_surface; + }; + +} // namespace ice::platform::win32::sdl2 diff --git a/source/code/platforms/platform_linux/private/linux_sdl2_platform_render_surface.cxx b/source/code/platforms/platform_linux/private/linux_sdl2_platform_render_surface.cxx new file mode 100644 index 000000000..b36b82c78 --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_sdl2_platform_render_surface.cxx @@ -0,0 +1,131 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#include "linux_sdl2_platform_render_surface.hxx" +#include +#include +#include +#include + +#include "linux_sdl2.hxx" + +namespace ice::platform::linux::sdl2 +{ + + RenderSurface_WaylandX11SDL2::RenderSurface_WaylandX11SDL2() noexcept + { + SDL_InitSubSystem(SDL_INIT_VIDEO); + } + + RenderSurface_WaylandX11SDL2::~RenderSurface_WaylandX11SDL2() noexcept + { + ICE_ASSERT(_window == nullptr, "Render surface was not properly cleaned up!"); + if (_window != nullptr) + { + RenderSurface_WaylandX11SDL2::destroy(); + } + + SDL_VideoQuit(); + } + + auto RenderSurface_WaylandX11SDL2::create(ice::platform::RenderSurfaceParams surface_params) noexcept -> ice::Result + { + IPT_ZONE_SCOPED; + + if (_window != nullptr) + { + return E_RenderSurfaceAlreadyExisting; + } + + if (surface_params.dimensions.x == 0 || surface_params.dimensions.y == 0) + { + return E_InvalidArgument; + } + + using ice::render::RenderDriverAPI; + ice::i32 creation_flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; + ice::StaticString<64> window_title{ surface_params.window_title }; + if (ice::string::empty(window_title)) + { + if (surface_params.driver == RenderDriverAPI::Vulkan) + { + window_title = ice::String{ "Iceshard (SDL2, Vulkan, " }; + ice::string::push_back(window_title, SDL_GetCurrentVideoDriver()); + ice::string::push_back(window_title, ")"); + creation_flags |= SDL_WINDOW_VULKAN; + } + else if (surface_params.driver == RenderDriverAPI::OpenGL) + { + window_title = ice::String{ "Iceshard (SDL2, OpenGL)" }; + creation_flags |= SDL_WINDOW_OPENGL; + } + } + + _window = SDL_CreateWindow( + ice::string::data(window_title), + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + surface_params.dimensions.x, + surface_params.dimensions.y, + creation_flags + ); + return S_Success; + } + + bool RenderSurface_WaylandX11SDL2::get_surface(ice::render::SurfaceInfo& out_surface_info) noexcept + { + if (_window == nullptr) + { + return false; + } + + SDL_SysWMinfo wm_info{}; + SDL_VERSION(&wm_info.version); + SDL_GetWindowWMInfo(_window, &wm_info); + +#if defined(SDL_VIDEO_DRIVER_X11) + ICE_LOG(LogSeverity::Info, LogTag::Core, "Checking for 'X11' video driver..."); + if (wm_info.subsystem == SDL_SYSWM_X11) + { + ICE_LOG(LogSeverity::Info, LogTag::Core, "Selected 'X11' video driver"); + out_surface_info.type = ice::render::SurfaceType::X11_Window; + out_surface_info.x11.display = wm_info.info.x11.display; + out_surface_info.x11.window = wm_info.info.x11.window; + } +#endif +#if defined(SDL_VIDEO_DRIVER_WAYLAND) + ICE_LOG_IF( + out_surface_info.type == ice::render::SurfaceType::Unknown, + LogSeverity::Info, LogTag::Core, + "Checking for 'Wayland' video driver..." + ); + if (wm_info.subsystem == SDL_SYSWM_WAYLAND) + { + ICE_LOG(LogSeverity::Info, LogTag::Core, "Selected 'Wayland' video driver"); + out_surface_info.type = ice::render::SurfaceType::Wayland_Window; + out_surface_info.wayland.surface = wm_info.info.wl.surface; + out_surface_info.wayland.display = wm_info.info.wl.display; + } +#endif + +#if !defined(SDL_VIDEO_DRIVER_WAYLAND) and !defined(SDL_VIDEO_DRIVER_X11) + ICE_LOG( + LogSeverity::Error, LogTag::Core, + "The currently used SDL2 package does not support Wayland nor X11 surfaces!" + ); +#else + ICE_LOG_IF( + out_surface_info.type == ice::render::SurfaceType::Unknown, + LogSeverity::Error, LogTag::Core, + "Unrecognized SDL2 Surface type!" + ); +#endif + return out_surface_info.type != ice::render::SurfaceType::Unknown; + } + + void RenderSurface_WaylandX11SDL2::destroy() noexcept + { + SDL_DestroyWindow(ice::exchange(_window, nullptr)); + } + +} // namespace ice::platform::win32::sdl2 diff --git a/source/code/platforms/platform_linux/private/linux_sdl2_platform_render_surface.hxx b/source/code/platforms/platform_linux/private/linux_sdl2_platform_render_surface.hxx new file mode 100644 index 000000000..0960a934c --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_sdl2_platform_render_surface.hxx @@ -0,0 +1,28 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#pragma once +#include +#include +#include "linux_sdl2.hxx" + +namespace ice::platform::linux::sdl2 +{ + + class RenderSurface_WaylandX11SDL2 final : public RenderSurface + { + public: + RenderSurface_WaylandX11SDL2() noexcept; + ~RenderSurface_WaylandX11SDL2() noexcept; + + auto create(ice::platform::RenderSurfaceParams surface_params) noexcept -> ice::Result override; + auto get_dimensions() const noexcept -> ice::vec2u override { return {}; } + bool get_surface(ice::render::SurfaceInfo& out_surface_info) noexcept override; + void destroy() noexcept override; + + private: + ice::render::RenderDriverAPI _render_driver; + SDL_Window* _window; + }; + +} // namespace ice::platform::win32::sdl2 diff --git a/source/code/platforms/platform_linux/private/linux_sdl2_utils.cxx b/source/code/platforms/platform_linux/private/linux_sdl2_utils.cxx new file mode 100644 index 000000000..4b97f42b0 --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_sdl2_utils.cxx @@ -0,0 +1,278 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#include "linux_sdl2_utils.hxx" +#include +#include + +namespace ice::platform::linux::sdl2 +{ + + auto map_sdl_mouse_button(ice::u8 button_id) noexcept -> ice::input::MouseInput + { + using ice::input::MouseInput; + + switch (button_id) + { + case SDL_BUTTON_LEFT: + return MouseInput::ButtonLeft; + case SDL_BUTTON_RIGHT: + return MouseInput::ButtonRight; + case SDL_BUTTON_MIDDLE: + return MouseInput::ButtonMiddle; + case SDL_BUTTON_X1: + return MouseInput::ButtonCustom0; + case SDL_BUTTON_X2: + return MouseInput::ButtonCustom1; + default: + break; + } + return MouseInput::Unknown; + } + + auto map_sdl_key_scancode(SDL_Scancode scancode) noexcept -> ice::input::KeyboardKey + { + using ice::input::KeyboardKey; + + if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) + { + return KeyboardKey(static_cast(KeyboardKey::KeyA) + (scancode - SDL_SCANCODE_A)); + } + + if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) + { + return KeyboardKey(static_cast(KeyboardKey::KeyF1) + (scancode - SDL_SCANCODE_F1)); + } + + if (scancode == SDL_SCANCODE_0) + { + return KeyboardKey(static_cast(KeyboardKey::Key0)); + } + else if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_9) + { + return KeyboardKey(static_cast(KeyboardKey::Key1) + (scancode - SDL_SCANCODE_1)); + } + + switch (scancode) + { + case SDL_SCANCODE_RETURN: return KeyboardKey::Return; + case SDL_SCANCODE_ESCAPE: return KeyboardKey::Escape; + case SDL_SCANCODE_BACKSPACE: return KeyboardKey::Backspace; + case SDL_SCANCODE_TAB: return KeyboardKey::Tab; + case SDL_SCANCODE_SPACE: return KeyboardKey::Space; + case SDL_SCANCODE_APOSTROPHE: return KeyboardKey::Quote; + case SDL_SCANCODE_COMMA: return KeyboardKey::Comma; + case SDL_SCANCODE_MINUS: return KeyboardKey::Minus; + case SDL_SCANCODE_PERIOD: return KeyboardKey::Period; + case SDL_SCANCODE_SLASH: return KeyboardKey::Slash; + + case SDL_SCANCODE_SEMICOLON: return KeyboardKey::SemiColon; + case SDL_SCANCODE_EQUALS: return KeyboardKey::Equals; + case SDL_SCANCODE_LEFTBRACKET: return KeyboardKey::LeftBracket; + case SDL_SCANCODE_BACKSLASH: return KeyboardKey::BackSlash; + case SDL_SCANCODE_RIGHTBRACKET: return KeyboardKey::RightBracket; + case SDL_SCANCODE_GRAVE: return KeyboardKey::BackQuote; + + case SDL_SCANCODE_DELETE: return KeyboardKey::Delete; + //case SDL_SCANCODE_CAPSLOCK: return KeyboardKey::CapsLock; + + case SDL_SCANCODE_UP: return KeyboardKey::Up; + case SDL_SCANCODE_DOWN: return KeyboardKey::Down; + case SDL_SCANCODE_LEFT: return KeyboardKey::Left; + case SDL_SCANCODE_RIGHT: return KeyboardKey::Right; + default: + break; + } + + //case SDL_SCANCODE_EXCLAIM: return KeyboardKey::Exclaim; + //case SDL_SCANCODE_QUOTEDBL: return KeyboardKey::QuoteDouble; + //case SDL_SCANCODE_HASH: return KeyboardKey::Hash; + //case SDL_SCANCODE_PERCENT: return KeyboardKey::Percent; + //case SDL_SCANCODE_AMPERSAND: return KeyboardKey::Ampersand; + //case SDL_SCANCODE_LEFTPAREN: return KeyboardKey::LeftParen; + //case SDL_SCANCODE_RIGHTPAREN: return KeyboardKey::RightParen; + //case SDL_SCANCODE_ASTERISK: return KeyboardKey::Asteriks; + //case SDL_SCANCODE_PLUS: return KeyboardKey::Plus; + //case SDL_SCANCODE_COLON: return KeyboardKey::Colon; + //case SDL_SCANCODE_LESS: return KeyboardKey::Less; + //case SDL_SCANCODE_GREATER: return KeyboardKey::Greater; + //case SDL_SCANCODE_QUESTION: return KeyboardKey::Question; + //case SDL_SCANCODE_AT: return KeyboardKey::At; + //case SDL_SCANCODE_CARET: return KeyboardKey::Caret; + //case SDL_SCANCODE_UNDERSCORE: return KeyboardKey::Underscore; + + return KeyboardKey::Unknown; + } + + auto map_sdl_mod_scancode(SDL_Scancode scancode) noexcept -> ice::input::KeyboardMod + { + using ice::input::KeyboardMod; + + if (scancode == SDL_Scancode::SDL_SCANCODE_LCTRL) + { + return KeyboardMod::CtrlLeft; + } + if (scancode == SDL_Scancode::SDL_SCANCODE_LSHIFT) + { + return KeyboardMod::ShiftLeft; + } + if (scancode == SDL_Scancode::SDL_SCANCODE_LALT) + { + return KeyboardMod::AltLeft; + } + if (scancode == SDL_Scancode::SDL_SCANCODE_LGUI) + { + return KeyboardMod::GuiLeft; + } + + if (scancode == SDL_Scancode::SDL_SCANCODE_RCTRL) + { + return KeyboardMod::CtrlRight; + } + if (scancode == SDL_Scancode::SDL_SCANCODE_RSHIFT) + { + return KeyboardMod::ShiftRight; + } + if (scancode == SDL_Scancode::SDL_SCANCODE_RALT) + { + return KeyboardMod::AltRight; + } + if (scancode == SDL_Scancode::SDL_SCANCODE_RGUI) + { + return KeyboardMod::GuiRight; + } + + if (scancode == SDL_Scancode::SDL_SCANCODE_MODE) + { + return KeyboardMod::Mode; + } + if (scancode == SDL_Scancode::SDL_SCANCODE_NUMLOCKCLEAR) + { + return KeyboardMod::NumLock; + } + if (scancode == SDL_Scancode::SDL_SCANCODE_CAPSLOCK) + { + return KeyboardMod::CapsLock; + } + + return KeyboardMod::None; + } + + + void mouse_input_events( + ice::input::DeviceEventQueue& input_queue, + SDL_Event const& sdl_event + ) noexcept + { + using namespace ice::input; + + if (sdl_event.type == SDL_EventType::SDL_MOUSEMOTION) + { + ice::i32 pos[2]; + + if (SDL_GetRelativeMouseMode()) + { + pos[0] = sdl_event.motion.xrel; + pos[1] = sdl_event.motion.yrel; + } + else + { + pos[0] = sdl_event.motion.x; + pos[1] = sdl_event.motion.y; + } + + if (pos[0] != 0 || pos[1] != 0) + { + input_queue.push( + make_device_handle(DeviceType::Mouse, DeviceIndex(sdl_event.motion.which)), + DeviceMessage::MousePositionX, + pos[0] + ); + input_queue.push( + make_device_handle(DeviceType::Mouse, DeviceIndex(sdl_event.motion.which)), + DeviceMessage::MousePositionY, + pos[1] + ); + } + } + else if (sdl_event.type == SDL_MOUSEBUTTONDOWN) + { + input_queue.push( + make_device_handle(DeviceType::Mouse, DeviceIndex(sdl_event.motion.which)), + DeviceMessage::MouseButtonDown, + map_sdl_mouse_button(sdl_event.button.button) + ); + } + else if (sdl_event.type == SDL_MOUSEBUTTONUP) + { + input_queue.push( + make_device_handle(DeviceType::Mouse, DeviceIndex(sdl_event.motion.which)), + DeviceMessage::MouseButtonUp, + map_sdl_mouse_button(sdl_event.button.button) + ); + } + else if (sdl_event.type == SDL_MOUSEWHEEL) + { + input_queue.push( + make_device_handle(DeviceType::Mouse, DeviceIndex(sdl_event.motion.which)), + DeviceMessage::MouseWheel, + sdl_event.wheel.y + ); + } + } + + void keyboard_input_events( + ice::input::DeviceEventQueue& input_queue, + SDL_Event const& sdl_event + ) noexcept + { + using namespace ice::input; + [[maybe_unused]] + DeviceHandle const device = make_device_handle(DeviceType::Keyboard, DeviceIndex{ 0 }); + + KeyboardKey const key = map_sdl_key_scancode(sdl_event.key.keysym.scancode); + if (key != KeyboardKey::Unknown) + { + if (sdl_event.key.type == SDL_KEYDOWN) + { + input_queue.push( + device, + DeviceMessage::KeyboardButtonDown, + key + ); + } + else if (sdl_event.key.type == SDL_KEYUP) + { + input_queue.push( + device, + DeviceMessage::KeyboardButtonUp, + key + ); + } + } + else + { + KeyboardMod const mod = map_sdl_mod_scancode(sdl_event.key.keysym.scancode); + if (mod != KeyboardMod::None) + { + if (sdl_event.key.type == SDL_KEYDOWN) + { + input_queue.push( + device, + DeviceMessage::KeyboardModifierDown, + mod + ); + } + else if (sdl_event.key.type == SDL_KEYUP) + { + input_queue.push( + device, + DeviceMessage::KeyboardModifierUp, + mod + ); + } + } + } + } + +} // namespace ice::platform::linux::sdl2 diff --git a/source/code/platforms/platform_linux/private/linux_sdl2_utils.hxx b/source/code/platforms/platform_linux/private/linux_sdl2_utils.hxx new file mode 100644 index 000000000..6fce2e7b7 --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_sdl2_utils.hxx @@ -0,0 +1,21 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#pragma once +#include +#include "linux_sdl2.hxx" + +namespace ice::platform::linux::sdl2 +{ + + void mouse_input_events( + ice::input::DeviceEventQueue& input_queue, + SDL_Event const& sdl_event + ) noexcept; + + void keyboard_input_events( + ice::input::DeviceEventQueue& input_queue, + SDL_Event const& sdl_event + ) noexcept; + +} // namespace ice::platform::linux::sdl2 diff --git a/source/code/platforms/platform_linux/private/linux_storage.cxx b/source/code/platforms/platform_linux/private/linux_storage.cxx new file mode 100644 index 000000000..91e8d239d --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_storage.cxx @@ -0,0 +1,191 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#include "linux_storage.hxx" + +#include +#include +#include +#include +#include +#include + +namespace ice::platform::linux +{ + + namespace detail + { + + static constexpr ice::String FolderID_Desktop = "XDG_DESKTOP_DIR"; + static constexpr ice::String FolderID_Download = "XDG_DOWNLOAD_DIR"; + static constexpr ice::String FolderID_Templates = "XDG_TEMPLATES_DIR"; + static constexpr ice::String FolderID_Publicshare = "XDG_PUBLICSHARE_DIR"; + static constexpr ice::String FolderID_Documents = "XDG_DOCUMENTS_DIR"; + static constexpr ice::String FolderID_Music = "XDG_MUSIC_DIR"; + static constexpr ice::String FolderID_Pictures = "XDG_PICTURES_DIR"; + static constexpr ice::String FolderID_SavedGames = "XDG_SAVEDGAMES_DIR"; + static constexpr ice::String FolderID_Videos = "XDG_VIDEOS_DIR"; + + void create_known_path(ice::HeapString<>& out_path, ice::String home, ice::String known_path, ice::String appname) noexcept + { + ice::string::push_format(out_path, "{}/{}/{}", home, known_path, appname); + ice::path::normalize(out_path); + } + + void find_known_path(ice::HeapString<>& out_path, ice::String contents, ice::String home, ice::String known_path_id, ice::String fallback, ice::String appname) noexcept + { + ice::string::for_each_split( + contents, + "\n", + [&](ice::String line) noexcept + { + if (ice::string::empty(line) || line[0] == '#') + { + return true; + } + + ice::ucount const idassignment = ice::string::find_first_of(line, '='); + if (idassignment == ice::String_NPos) + { + ICE_LOG(LogSeverity::Warning, LogTag::Core, "Improperly formatted line: {}", line); + return true; + } + + ice::String const id = ice::string::substr(line, 0, idassignment); + ice::ucount const value_start = ice::string::find_first_of(line, '"', idassignment); + ice::ucount const value_end = ice::string::find_last_of(line, '"'); + if (value_end == value_start) + { + ICE_LOG(LogSeverity::Warning, LogTag::Core, "Improperly formatted line: {}", line); + return true; + } + + ice::String const value = ice::string::substr(line, value_start + 1, (value_end - value_start) - 1); + ICE_LOG(LogSeverity::Debug, LogTag::Core, "- {} = {}", id, value); + + if (id == known_path_id) + { + create_known_path(out_path, home, ice::string::substr(value, 5), appname); + ICE_LOG(LogSeverity::Info, LogTag::Core, "Known path ID {} found: {}", known_path_id, out_path); + return false; + } + return true; + } + ); + + if (ice::string::empty(out_path)) + { + create_known_path(out_path, home, fallback, appname); + ICE_LOG(LogSeverity::Info, LogTag::Core, "Known path ID {} not found, using fallback: {}", known_path_id, out_path); + } + } + + bool create_directory(ice::String path) noexcept + { + return false; + } + + } // namespace detail + + LinuxStorage::LinuxStorage( + ice::Allocator& alloc, + ice::Span params + ) noexcept + : _allocator{ alloc } + , _config_file{ alloc } + , _home_location{ alloc } + , _save_location{ alloc } + , _cache_location{ alloc } + , _temp_location{ alloc } + , _pictures_location{ alloc } + , _other_location{ alloc } + { + // Get HOME securely + char const* home_env = secure_getenv("HOME"); + ICE_ASSERT_CORE(home_env != nullptr); + _home_location = home_env; + + + char const* app_name = "iceshard"; + for (ice::Shard param : params) + { + if (param == Shard_StorageAppName) + { + ice::shard_inspect(param, app_name); + // TODO: Warning if shard value was not valid. + } + } + + // Hardcoded paths + _config_file = ice::native_file::path_from_strings(alloc, _home_location, ".config/user-dirs.dirs"); + _temp_location = ice::native_file::path_from_strings(alloc, "/tmp", app_name); + _cache_location = ice::native_file::path_from_strings(alloc, _home_location, ".iceshard", app_name); + + // TODO: Use a special function that can be overriden by the host app? + reload_paths(app_name); + } + + bool LinuxStorage::set_appname(ice::String name) noexcept + { + // reload_paths(name); + + // TODO: Handle errors? + // detail::create_directory(_save_location); + // detail::create_directory(_cache_location); + // detail::create_directory(_pictures_location); + // detail::create_directory(_other_location); + return false; + } + + auto LinuxStorage::data_locations() const noexcept -> ice::Span + { + static ice::String paths[]{ + ice::app::directory() + }; + return paths; + } + + auto LinuxStorage::dylibs_location() const noexcept -> ice::String + { + return ice::path::directory(ice::app::location()); + } + + void LinuxStorage::reload_paths(ice::String appname) noexcept + { + ice::string::clear(_save_location); + ice::string::clear(_pictures_location); + ice::string::clear(_other_location); + + ICE_LOG(LogSeverity::Info, LogTag::Core, "Checking for XDG {} file...", _config_file); + if (ice::native_file::exists_file(_config_file)) + { + ICE_LOG(LogSeverity::Info, LogTag::Core, "Found, parsing for known directories..."); + ice::native_file::File const file = ice::native_file::open_file(_config_file); + ice::Memory const contents_mem = _allocator.allocate(ice::native_file::sizeof_file(file)); + ice::String contents = ice::String{ reinterpret_cast(contents_mem.location), ice::ucount(contents_mem.size.value) }; + + if (ice::native_file::read_file(file, contents_mem.size, contents_mem) > 0_B) + { + detail::find_known_path(_save_location, contents, _home_location, detail::FolderID_SavedGames, "SavedGames", appname); + detail::find_known_path(_pictures_location, contents, _home_location, detail::FolderID_Pictures, "Pictures", appname); + detail::find_known_path(_other_location, contents, _home_location, detail::FolderID_Documents, "Documents", appname); + } + + _allocator.deallocate(contents_mem); + } + else + { + ICE_LOG(LogSeverity::Info, LogTag::Core, "File does not exist, falling back to expected defaults", _config_file); + detail::create_known_path(_save_location, _home_location, "SavedGames", appname); + detail::create_known_path(_pictures_location, _home_location, "Pictures", appname); + detail::create_known_path(_other_location, _home_location, "Documents", appname); + } + + ice::native_file::create_directory(_temp_location); + ice::native_file::create_directory(_cache_location); + ice::native_file::create_directory(_save_location); + ice::native_file::create_directory(_pictures_location); + ice::native_file::create_directory(_other_location); + } + +} // namespace ice::platform::linux diff --git a/source/code/platforms/platform_linux/private/linux_storage.hxx b/source/code/platforms/platform_linux/private/linux_storage.hxx new file mode 100644 index 000000000..877093f89 --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_storage.hxx @@ -0,0 +1,57 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#pragma once +#include +#include +#include +#include + +namespace ice::platform::linux +{ + + class LinuxStorage final : public ice::platform::StoragePaths + { + public: + LinuxStorage(ice::Allocator& alloc, ice::Span params) noexcept; + + //! \brief Requests the storage to alter most paths by appeding the given string at the end of them. + //! \note The storage implementation is not required to alter all paths. + //! Additionally the 'data_location()' path will never be altered by this operation. + //! + //! \param[in] name The name to be appended to all paths. + //! \return 'true' If the operation was supported and successful. + bool set_appname(ice::String name) noexcept; + + auto data_locations() const noexcept -> ice::Span override; + auto save_location() const noexcept -> ice::String override { return _save_location; } + auto cache_location() const noexcept -> ice::String override { return _cache_location; } + + auto temp_location() const noexcept -> ice::String override { return _temp_location; } + auto usercontent_location(UserContentType content_type) const noexcept -> ice::String override { return {}; } + + auto dylibs_location() const noexcept -> ice::String override; + + [[deprecated]] + auto internal_data() const noexcept -> ice::String { return {}; } + [[deprecated]] + auto external_data() const noexcept -> ice::String { return {}; } + [[deprecated]] + auto save_data() const noexcept -> ice::String { return {}; } + + protected: + void reload_paths(ice::String appname) noexcept; + + private: + ice::Allocator& _allocator; + ice::native_file::HeapFilePath _config_file; + ice::HeapString<> _home_location; + ice::HeapString<> _save_location; + ice::HeapString<> _cache_location; + ice::HeapString<> _temp_location; + ice::HeapString<> _pictures_location; + ice::HeapString<> _other_location; + }; + + +} // namespace ice diff --git a/source/code/platforms/platform_linux/private/linux_threads.cxx b/source/code/platforms/platform_linux/private/linux_threads.cxx new file mode 100644 index 000000000..ae37c07c8 --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_threads.cxx @@ -0,0 +1,72 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#include "linux_threads.hxx" +#include +#include +#include +#include + +namespace ice::platform::linux +{ + + auto get_num_cores(ice::Allocator& alloc) noexcept -> ice::ucount + { + ice::ucount const hw_concurrency = sysconf(_SC_NPROCESSORS_ONLN); + ICE_LOG(LogSeverity::Info, LogTag::System, "Logical Processors: {}", hw_concurrency); + return hw_concurrency; + } + + LinuxThreads::LinuxThreads( + ice::Allocator& alloc, + ice::Span params + ) noexcept + : queue_main{ } + , queue_gfx{ } + , queue_tasks{ } + , _scheduler_main{ queue_main } + , _scheduler_gfx{ queue_gfx } + , _scheduler_tasks{ queue_tasks } + , _threads{ } + , _aioport{ ice::native_aio::aio_open(alloc, { .worker_limit = 2, .debug_name = "ice.aio-port" }) } + { + ice::ucount const hw_concurrency = ice::min(get_num_cores(alloc), 8u); // max 8 tasks threads + ice::ucount tp_size = ice::max(hw_concurrency, 2u); // min 2 task threads + + for (ice::Shard const option : params) + { + if (option == Shard_ThreadPoolSize) + { + tp_size = ice::shard_shatter(option, tp_size); + } + } + + ice::UniquePtr gfx_thread = ice::create_thread( + alloc, queue_gfx, + TaskThreadInfo{ + .exclusive_queue = true, + .debug_name = "ice.gfx" + } + ); + + // One could force threadpool size to 0 from the params. + _threads = ice::create_thread_pool( + alloc, queue_tasks, + TaskThreadPoolCreateInfo { + .thread_count = tp_size, + .aioport = _aioport, + .debug_name_format = "ice.thread {}", + } + ); + + // Attache the graphics thread to the threadpool so we don't need to manage it ourselfs. + _threads->attach_thread("platform.graphics-thread"_sid, ice::move(gfx_thread)); + } + + LinuxThreads::~LinuxThreads() noexcept + { + // We close the port first (threads implicitly) to ensure we don't get stuck on io-port waiting indefinitely. + ice::native_aio::aio_close(_aioport); + } + +} // namespace ice::platform::win32 diff --git a/source/code/platforms/platform_linux/private/linux_threads.hxx b/source/code/platforms/platform_linux/private/linux_threads.hxx new file mode 100644 index 000000000..16eb38757 --- /dev/null +++ b/source/code/platforms/platform_linux/private/linux_threads.hxx @@ -0,0 +1,45 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ice::platform::linux +{ + + class LinuxThreads final : public ice::platform::Threads + { + public: + LinuxThreads( + ice::Allocator& alloc, + ice::Span params + ) noexcept; + ~LinuxThreads() noexcept override; + + auto main() noexcept -> ice::TaskScheduler& override { return _scheduler_main; } + auto graphics() noexcept -> ice::TaskScheduler& override { return _scheduler_gfx; } + + auto threadpool() noexcept -> ice::TaskScheduler& override { return _scheduler_tasks; } + auto threadpool_size() const noexcept -> ice::u32 override { return _threads->managed_thread_count(); } + auto threadpool_object() noexcept -> ice::TaskThreadPool* override { return _threads.get(); } + auto aio_port() const noexcept -> ice::native_aio::AIOPort override { return _aioport; } + + ice::TaskQueue queue_main; + ice::TaskQueue queue_gfx; + ice::TaskQueue queue_tasks; + + private: + ice::TaskScheduler _scheduler_main; + ice::TaskScheduler _scheduler_gfx; + ice::TaskScheduler _scheduler_tasks; + ice::UniquePtr _threads; + + ice::native_aio::AIOPort _aioport; + }; + +} // namespace ice::platform::linux diff --git a/source/code/platforms/platform_win32/private/win32_app_info.cxx b/source/code/platforms/platform_win32/private/win32_app_info.cxx deleted file mode 100644 index 1df0b93b8..000000000 --- a/source/code/platforms/platform_win32/private/win32_app_info.cxx +++ /dev/null @@ -1,60 +0,0 @@ -/// Copyright 2022 - 2025, Dandielo -/// SPDX-License-Identifier: MIT - -#include - -#if ISP_WINDOWS -#include -#include -#include -#include -#include - -namespace ice::app -{ - - auto location() noexcept -> ice::String - { - static ice::StaticString<256> app_location = []() noexcept - { - ice::StaticString<256, ice::wchar> location_wide{ L"" }; - DWORD const path_size = GetModuleFileNameW(NULL, ice::string::begin(location_wide), ice::string::capacity(location_wide)); - ice::string::resize(location_wide, path_size); - - ice::StackAllocator_1024 stack_alloc; - ice::HeapString<> location_utf8{ stack_alloc }; - ice::wide_to_utf8_append(location_wide, location_utf8); - - return ice::StaticString<256>{ location_utf8 }; - }(); - - return app_location; - } - - auto directory() noexcept -> ice::String - { - static ice::String app_directory = ice::path::directory(location()); - return app_directory; - } - - auto workingdir() noexcept -> ice::String - { - static ice::StaticString<256> working_dir = []() noexcept - { - ice::StaticString<256, ice::wchar> location_wide{ L"" }; - DWORD const path_size = GetCurrentDirectoryW(ice::string::capacity(location_wide), ice::string::begin(location_wide)); - ice::string::resize(location_wide, path_size); - - ice::StackAllocator_1024 stack_alloc; - ice::HeapString<> location_utf8{ stack_alloc }; - ice::wide_to_utf8_append(location_wide, location_utf8); - - return ice::StaticString<256>{ location_utf8 }; - }(); - - return working_dir; - } - -} // namespace ice::app - -#endif // ISP_WINDOWS diff --git a/source/code/platforms/platform_win32/private/win32_sdl2_platform_render_surface.cxx b/source/code/platforms/platform_win32/private/win32_sdl2_platform_render_surface.cxx index d68b130ec..b87983655 100644 --- a/source/code/platforms/platform_win32/private/win32_sdl2_platform_render_surface.cxx +++ b/source/code/platforms/platform_win32/private/win32_sdl2_platform_render_surface.cxx @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT #include "win32_sdl2_platform_render_surface.hxx" diff --git a/source/code/projects.bff b/source/code/projects.bff index ab031f6b2..aacae4c3b 100644 --- a/source/code/projects.bff +++ b/source/code/projects.bff @@ -23,6 +23,7 @@ #include "platforms/application/application.bff" #include "platforms/platform/platform.bff" #include "platforms/platform_win32/platform_win32.bff" +#include "platforms/platform_linux/platform_linux.bff" #include "platforms/platform_android/platform_android.bff" #include "platforms/platform_webasm/platform_webasm.bff" diff --git a/source/code/systems/render_system/private/render_module.cxx b/source/code/systems/render_system/private/render_module.cxx index 8370fea32..4f5541a99 100644 --- a/source/code/systems/render_system/private/render_module.cxx +++ b/source/code/systems/render_system/private/render_module.cxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #include diff --git a/source/code/systems/render_system/public/ice/render/render_module.hxx b/source/code/systems/render_system/public/ice/render/render_module.hxx index cb9625aba..1b9433c92 100644 --- a/source/code/systems/render_system/public/ice/render/render_module.hxx +++ b/source/code/systems/render_system/public/ice/render/render_module.hxx @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2024, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT #pragma once diff --git a/source/code/systems/render_system/public/ice/render/render_surface.hxx b/source/code/systems/render_system/public/ice/render/render_surface.hxx index dbe991282..a5e9f3612 100644 --- a/source/code/systems/render_system/public/ice/render/render_surface.hxx +++ b/source/code/systems/render_system/public/ice/render/render_surface.hxx @@ -15,15 +15,18 @@ namespace ice::render enum class SurfaceType { + Unknown, Win32_Window, UWP_Window, + Wayland_Window, + X11_Window, Android_NativeWindow, HTML5_DOMCanvas }; struct SurfaceInfo { - SurfaceType type; + SurfaceType type = SurfaceType::Unknown; union { struct @@ -48,6 +51,18 @@ namespace ice::render void* native_window; void* reserved[1]; } android; + + struct + { + void* surface; + void* display; + } wayland; + + struct + { + unsigned long window; + void* display; + } x11; }; }; diff --git a/source/code/systems/resource_system/private/resource_filesystem_baked.cxx b/source/code/systems/resource_system/private/resource_filesystem_baked.cxx index b040f3cf9..4b01cee53 100644 --- a/source/code/systems/resource_system/private/resource_filesystem_baked.cxx +++ b/source/code/systems/resource_system/private/resource_filesystem_baked.cxx @@ -88,8 +88,7 @@ namespace ice ice::Allocator& alloc, ice::ResourceFormatHeader const& header, ice::HeapString<> origin, - ice::HeapString<> name, - ice::Memory metadata + ice::HeapString<> name ) noexcept : _allocator{ alloc } , _header{ header } @@ -213,20 +212,13 @@ namespace ice ICE_ASSERT_CORE(read >= 0_B); ice::string::push_back(utf8_uri, { temp, header.name_size }); - ice::Memory metadata = alloc.allocate(ice::usize{ header.meta_size }); - read = ice::native_file::read_file( - file, {header.meta_offset}, {header.meta_size}, metadata - ); - ICE_ASSERT_CORE(read >= 0_B); - return ice::create_resource_object( alloc, provider, alloc, header, ice::move(utf8_file_path), - ice::move(utf8_uri), - metadata + ice::move(utf8_uri) ); } diff --git a/source/code/systems/resource_system/private/resource_filesystem_baked.hxx b/source/code/systems/resource_system/private/resource_filesystem_baked.hxx index 7d1720955..bb275e985 100644 --- a/source/code/systems/resource_system/private/resource_filesystem_baked.hxx +++ b/source/code/systems/resource_system/private/resource_filesystem_baked.hxx @@ -23,8 +23,7 @@ namespace ice ice::Allocator& alloc, ice::ResourceFormatHeader const& header, ice::HeapString<> origin, - ice::HeapString<> name, - ice::Memory metadata + ice::HeapString<> name ) noexcept; ~BakedFileResource() noexcept override; diff --git a/source/code/systems/resource_system/private/resource_filesystem_loose.cxx b/source/code/systems/resource_system/private/resource_filesystem_loose.cxx index 9e8f5170f..4ae6dfc3e 100644 --- a/source/code/systems/resource_system/private/resource_filesystem_loose.cxx +++ b/source/code/systems/resource_system/private/resource_filesystem_loose.cxx @@ -147,6 +147,7 @@ namespace ice , _metasize{ meta_size } , _datasize{ data_size } { + ICE_ASSERT_CORE(_metasize <= 10_MiB); } LooseFilesResource::~LooseFilesResource() noexcept diff --git a/source/code/systems/resource_system/private/resource_provider_filelist.cxx b/source/code/systems/resource_system/private/resource_provider_filelist.cxx index 07471ee59..45fa19508 100644 --- a/source/code/systems/resource_system/private/resource_provider_filelist.cxx +++ b/source/code/systems/resource_system/private/resource_provider_filelist.cxx @@ -58,7 +58,7 @@ namespace ice { for (FileSystemResource* res_entry : _resources) { - _named_allocator.destroy(res_entry); + ice::destroy_resource_object(_named_allocator, res_entry); } } diff --git a/source/code/systems/resource_system/private/resource_provider_filesystem.cxx b/source/code/systems/resource_system/private/resource_provider_filesystem.cxx index 8a46e65a8..011fb641e 100644 --- a/source/code/systems/resource_system/private/resource_provider_filesystem.cxx +++ b/source/code/systems/resource_system/private/resource_provider_filesystem.cxx @@ -206,7 +206,7 @@ namespace ice ice::HeapString<> uri_base{ _named_allocator }; ice::string::push_format(uri_base, "file://{}/", _virtual_hostname); - create_resource_from_baked_file(_named_allocator, *this, uri_base, filepath); + return create_resource_from_baked_file(_named_allocator, *this, uri_base, filepath); } else { diff --git a/source/code/systems/resource_system/private/resource_tracker.cxx b/source/code/systems/resource_system/private/resource_tracker.cxx index 95be43a24..27d6c71a0 100644 --- a/source/code/systems/resource_system/private/resource_tracker.cxx +++ b/source/code/systems/resource_system/private/resource_tracker.cxx @@ -439,8 +439,8 @@ namespace ice } } - ice::URI const urn_uri{ ice::Scheme_URN, name }; - if (ice::ResourceHandle res = tracker.find_resource(dynlib_uri); res != nullptr) + ice::URI const urn_uri{ ice::Scheme_URN, result }; + if (ice::ResourceHandle res = tracker.find_resource(urn_uri); res != nullptr) { return { alloc, ice::resource_origin(res) }; } diff --git a/source/code/test/private/game.cxx b/source/code/test/private/game.cxx index 3b15d0c30..2784bb2ea 100644 --- a/source/code/test/private/game.cxx +++ b/source/code/test/private/game.cxx @@ -43,7 +43,7 @@ static constexpr LogTagDefinition LogGame = ice::create_log_tag(LogTag::Game, "T struct GameTasksDebugAllocator final : public ice::Module { - static void set_allocator(ice::SnakeAllocator* allocator) noexcept + static void set_allocator(ice::ProxyAllocator* allocator) noexcept { _allocator_ptr = allocator; } @@ -64,10 +64,10 @@ struct GameTasksDebugAllocator final : public ice::Module ice::UniquePtr { @@ -250,18 +250,9 @@ struct TestModule : ice::Module IS_WORKAROUND_MODULE_INITIALIZATION(TestModule); }; -static constexpr ice::usize const Constant_SnakeAllocatorBuckets[]{ 2_KiB, 256_B }; -static constexpr ice::usize const Constant_SnakeAllocatorBlocks[]{ 32_KiB, 32_KiB }; - -static constexpr ice::SnakeAllocatorParams Constant_SnakeAllocatorParams{ - .chain_capacity = 10, - .block_sizes = Constant_SnakeAllocatorBlocks, - .bucket_sizes = Constant_SnakeAllocatorBuckets -}; - TestGame::TestGame(ice::Allocator& alloc) noexcept : _allocator{ alloc } - , _tasks_alloc{ _allocator, "tasks", Constant_SnakeAllocatorParams } + , _tasks_alloc{ _allocator, "tasks" } , _first_time{ true } { GameTasksDebugAllocator::set_allocator(&_tasks_alloc); @@ -355,538 +346,3 @@ auto TestGame::rendergraph(ice::gfx::GfxContext& device) noexcept -> ice::Unique return create_graph_runtime(_allocator, device, *_graph); } - -#if 0 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - - - -MyGame::MyGame(ice::Allocator& alloc, ice::Clock const& clock) noexcept - : _allocator{ alloc, "MyGame-alloc" } - , _clock{ clock } - , _current_engine{ nullptr } - , _ecs_archetypes{ _allocator } - , _ecs_block_pool{ _allocator } - , _ecs_storage{ } - , _game_gfx_pass{ } - , _test_world{ nullptr } -{ -} - -auto MyGame::graphics_world_template() const noexcept -> ice::WorldTemplate const& -{ - if constexpr (ice::build::is_debug || ice::build::is_develop) - { - static ice::StringID constexpr graphics_traits[]{ - ice::Constant_TraitName_RenderCamera, - ice::Constant_TraitName_RenderBase, - ice::Constant_TraitName_RenderTextureLoader, - ice::Constant_TraitName_RenderClear, - ice::Constant_TraitName_RenderSprites, - ice::Constant_TraitName_RenderTilemap, - ice::Constant_TraitName_RenderUI, - ice::Constant_TraitName_RenderGlyphs, - ice::Constant_TraitName_RenderPostprocess, - ice::Constant_TraitName_RenderDebug, - ice::Constant_TraitName_DevUI, - ice::Constant_TraitName_RenderFinish, - }; - - static ice::WorldTemplate graphics_world_template - { - .name = "ice.framework-base.default-graphics-world-template"_sid, - .traits = graphics_traits, - .entity_storage = _ecs_storage.get(), - }; - - graphics_world_template.entity_storage = _ecs_storage.get(); - return graphics_world_template; - } - else - { - static ice::StringID constexpr graphics_traits[]{ - ice::Constant_TraitName_RenderCamera, - ice::Constant_TraitName_RenderBase, - ice::Constant_TraitName_RenderTextureLoader, - ice::Constant_TraitName_RenderClear, - ice::Constant_TraitName_RenderSprites, - ice::Constant_TraitName_RenderTilemap, - ice::Constant_TraitName_RenderUI, - ice::Constant_TraitName_RenderGlyphs, - ice::Constant_TraitName_RenderPostprocess, - ice::Constant_TraitName_RenderDebug, - ice::Constant_TraitName_RenderFinish, - }; - - static ice::WorldTemplate graphics_world_template - { - .name = "ice.framework-base.default-graphics-world-template"_sid, - .traits = graphics_traits, - .entity_storage = _ecs_storage.get(), - }; - - graphics_world_template.entity_storage = _ecs_storage.get(); - return graphics_world_template; - } -} - -void MyGame::on_load_modules(ice::GameServices& services) noexcept -{ - ice::ModuleRegister& mod = services.module_registry(); - ice::ResourceTracker& res = services.resource_system(); - - // Maybe we should move the location where we can do replacements? - //{ - // [[maybe_unused]] - // ice::ResourceHandle* tsa = res.find_resource("file:/data/cotm/tileset_a.png"_uri); - // ice::wait_for(res.set_resource("urn:cotm/tileset_ab.png"_uri, tsa)); - //} - - ice::ResourceHandle* const pipelines_module = res.find_resource("urn:iceshard_pipelines.dll"_uri); - ice::ResourceHandle* const engine_module = res.find_resource("urn:iceshard.dll"_uri); - ice::ResourceHandle* const vulkan_module = res.find_resource("urn:vulkan_renderer.dll"_uri); - ice::ResourceHandle* const imgui_module = res.find_resource("urn:imgui_module.dll"_uri); - - ICE_ASSERT(pipelines_module != nullptr, "Missing `iceshard_pipelines.dll` module!"); - ICE_ASSERT(engine_module != nullptr, "Missing `iceshard.dll` module!"); - ICE_ASSERT(vulkan_module != nullptr, "Missing `vulkan_renderer.dll` module!"); - - mod.load_module(_allocator, ice::resource_origin(pipelines_module)); - mod.load_module(_allocator, ice::resource_origin(engine_module)); - mod.load_module(_allocator, ice::resource_origin(vulkan_module)); - - ice::register_asset_modules(_allocator, mod); - - if (imgui_module != nullptr) - { - mod.load_module(_allocator, ice::resource_origin(imgui_module)); - } -} - -void MyGame::on_app_startup(ice::Engine& engine) noexcept -{ - _current_engine = &engine; - ICE_LOG( - ice::LogSeverity::Debug, ice::LogTag::Game, - "Hello, world!" - ); - - { - ice::Allocator& alloc{ _allocator }; - - ice::ecs::EntityIndex entity_index{ alloc, 1000 }; - ice::ecs::ArchetypeIndex archetype_index{ alloc }; - ice::ecs::EntityOperations entity_operations{ alloc }; - - ice::ecs::Entity entities[10]; - entity_index.create_many(entities); - { - [[maybe_unused]] - auto a1 = archetype_index.register_archetype(ice::ecs::static_validation::Validation_Archetype_1); - - [[maybe_unused]] - auto a2 = archetype_index.register_archetype(ice::ecs::static_validation::Validation_Archetype_2); - - ice::ecs::EntityStorage entity_storage{ alloc, archetype_index }; - { - ice::ecs::queue_set_archetype(entity_operations, entities, a1, true); - - ice::ShardContainer shards{ _allocator }; - entity_storage.execute_operations(entity_operations, shards); - entity_operations.clear(); - - ice::ecs::queue_remove_entity(entity_operations, ice::shard_shatter(shards._data[3], ice::ecs::EntityHandle::Invalid)); - ice::ecs::queue_set_archetype(entity_operations, entities[3], a1); - entity_storage.execute_operations(entity_operations, shards); - } - } - entity_index.destroy_many(entities); - } - - _game_gfx_pass = ice::gfx::create_dynamic_pass(_allocator); - _game_gfx_pass->add_stage(ice::Constant_GfxStage_Clear); - _game_gfx_pass->add_stage(ice::Constant_GfxStage_DrawTilemap, ice::Constant_GfxStage_Clear); - _game_gfx_pass->add_stage(ice::Constant_GfxStage_DrawSprites, ice::Constant_GfxStage_DrawTilemap, ice::Constant_GfxStage_Clear); - _game_gfx_pass->add_stage(ice::Constant_GfxStage_Postprocess, ice::Constant_GfxStage_DrawSprites); - _game_gfx_pass->add_stage(ice::Constant_GfxStage_DrawUI, ice::Constant_GfxStage_Postprocess); - _game_gfx_pass->add_stage(ice::Constant_GfxStage_DrawGlyphs, ice::Constant_GfxStage_DrawUI); - _game_gfx_pass->add_stage(ice::Constant_GfxStage_DrawDebug, ice::Constant_GfxStage_DrawGlyphs); - - if constexpr (ice::build::is_debug || ice::build::is_develop) - { - _game_gfx_pass->add_stage(ice::Constant_GfxStage_DevUI, ice::Constant_GfxStage_Postprocess, ice::Constant_GfxStage_DrawDebug); - _game_gfx_pass->add_stage(ice::Constant_GfxStage_Finish, ice::Constant_GfxStage_Postprocess, ice::Constant_GfxStage_DevUI); - } - else - { - _game_gfx_pass->add_stage(ice::Constant_GfxStage_Finish, ice::Constant_GfxStage_Postprocess); - } - - // Initialize archetypes - { - engine.world_trait_archive().register_archetypes(_ecs_archetypes); - - _ecs_archetypes.register_archetype(ice::ecs::Constant_ArchetypeDefinition); - _ecs_archetypes.register_archetype(ice::ecs::Constant_ArchetypeDefinition); - _ecs_archetypes.register_archetype(ice::ecs::Constant_ArchetypeDefinition); - _ecs_archetypes.register_archetype(ice::ecs::Constant_ArchetypeDefinition); - _ecs_archetypes.register_archetype(ice::ecs::Constant_ArchetypeDefinition); - - _ecs_storage = ice::make_unique(_allocator, _allocator, _ecs_archetypes); - } - - ice::WorldManager& world_manager = engine.world_manager(); - - ice::StringID const world_traits[]{ - ice::Constant_TraitName_PhysicsBox2D, - ice::Constant_TraitName_Tilemap, - ice::Constant_TraitName_SpriteAnimator, - ice::Constant_TraitName_Actor, - ice::Constant_TraitName_GameUI, - }; - - ice::WorldTemplate const world_template{ - .name = "game.test_world"_sid, - .traits = ice::Span{ world_traits }, - .entity_storage = _ecs_storage.get(), - }; - - _test_world = world_manager.create_world(world_template); - _test_world->add_trait("game"_sid, this); - - _action_triggers = ice::action::create_trigger_database(_allocator); - _action_system = ice::action::create_action_system(_allocator, _clock, *_action_triggers); - ice::action::setup_common_triggers(*_action_triggers); - - using ice::operator""_shard; - using ice::operator""_shardid; - using namespace ice::action; - - Action my_action; - my_action.name = "jump-action"_sid; - my_action.stage_count = 1; - my_action.trigger_count = 3; - - ActionStage stages[]{ - ActionStage - { - .stage_shardid = "event/player/can_jump_again"_shardid, - .success_trigger_offset = 0, - .success_trigger_count = 1, - .failure_trigger_offset = 1, - .failure_trigger_count = 0, - .reset_trigger_offset = 2 - } - }; - - [[maybe_unused]] - ice::input::InputEvent event_w{ }; - - [[maybe_unused]] - ice::input::InputEvent event_a{ }; - - [[maybe_unused]] - ice::input::InputEvent event_d{ }; - - event_w.device = ice::input::make_device_handle(ice::input::DeviceType::Keyboard, ice::input::DeviceIndex{ 0 }); - event_a.device = ice::input::make_device_handle(ice::input::DeviceType::Keyboard, ice::input::DeviceIndex{ 0 }); - event_d.device = ice::input::make_device_handle(ice::input::DeviceType::Keyboard, ice::input::DeviceIndex{ 0 }); - - event_w.identifier = ice::input::input_identifier(ice::input::DeviceType::Keyboard, ice::input::KeyboardKey::KeyW, ice::input::key_identifier_base_value); - event_a.identifier = ice::input::input_identifier(ice::input::DeviceType::Keyboard, ice::input::KeyboardKey::KeyA, ice::input::key_identifier_base_value); - event_d.identifier = ice::input::input_identifier(ice::input::DeviceType::Keyboard, ice::input::KeyboardKey::KeyD, ice::input::key_identifier_base_value); - - event_w.value_type = ice::input::InputValueType::Button; - event_a.value_type = ice::input::InputValueType::Button; - event_d.value_type = ice::input::InputValueType::Button; - - event_w.value.button.state.pressed = true; - event_a.value.button.state.pressed = true; - event_d.value.button.state.pressed = true; - - ActionTrigger triggers[]{ - ActionTrigger - { - .name = "trigger.action-input-button"_sid, - .user_shard = "key"_shard | event_w - }, - ActionTrigger - { - .name = "trigger.action-input-button"_sid, - .user_shard = "key"_shard | event_a - }, - ActionTrigger - { - .name = "trigger.elapsed-time"_sid, - .user_shard = "time"_shard | ice::f32{ 0.4f } - } - }; - - my_action.stages = stages; - my_action.triggers = triggers; - _action_system->create_action(my_action.name, my_action); -} - -void MyGame::on_app_shutdown(ice::Engine& engine) noexcept -{ - _action_system = nullptr; - _action_triggers = nullptr; - - _test_world->remove_trait("game"_sid); - - ice::WorldManager& world_manager = engine.world_manager(); - world_manager.destroy_world("game.test_world"_sid); - - // Destroy ECS storage - _ecs_storage = nullptr; - _game_gfx_pass = nullptr; - - ICE_LOG( - ice::LogSeverity::Debug, ice::LogTag::Game, - "Goodbye, world!" - ); - _current_engine = nullptr; -} - -void MyGame::on_game_begin(ice::EngineRunner& runner) noexcept -{ - using ice::operator""_sid_hash; - - ice::vec2u extent = runner.graphics_device().swapchain().extent(); - - auto const player_arch = ice::ecs::Constant_Archetype; - - ice::ecs::Entity camera_entity = _current_engine->entity_index().create(); - ice::ecs::Entity player_entity = _current_engine->entity_index().create(); - - ice::Camera const camera{ - .name = "camera.default"_sid, - .position = { 0.f, 0.f, 0.f }, - .front = { 0.f, 0.f, -1.f } - }; - ice::CameraOrtho const orto_values{ - .left_right = { 0.f, (ice::f32)extent.x / 2.f }, - .bottom_top = { 0.f, (ice::f32)extent.y / 2.f }, - .near_far = { 0.1f, 100.f } - }; - - ice::ecs::queue_set_archetype_with_data( - runner.current_frame().entity_operations(), - camera_entity, - ice::ecs::Constant_Archetype, - camera, - orto_values - ); - - ice::ecs::queue_set_archetype_with_data( - runner.current_frame().entity_operations(), - player_entity, - player_arch, - ice::Animation{.animation = "cotm_idle"_sid_hash, .speed = 1.f / 60.f }, - ice::Actor{.type = ice::ActorType::Player }, - ice::Transform2DDynamic{.position = { 48.f * 2, 448.f, -1.f }, .scale = { 1.f, 0.f } }, - ice::PhysicsBody{.shape = ice::PhysicsShape::Capsule, .dimensions = { 16.f, 32.f }, .trait_data = nullptr }, - ice::PhysicsVelocity{.velocity = { 0.1f, 0.f } }, - ice::Sprite{.material = "local/cotm_hero" }, - ice::SpriteTile{.material_tile = { 0, 0 } } - ); - - ice::String const* tilemap_asset = runner.current_frame().storage().create_named_object( - "tilemap_asset_name"_sid, - "local/maps/level_0" - ); - - ice::Shard shards[]{ - ice::Shard_WorldActivate | _test_world, - ice::Shard_LoadTileMap | tilemap_asset, - }; - ice::shards::push_back( - runner.current_frame().shards(), - shards - ); -} - -void MyGame::on_game_end() noexcept -{ -} - -void MyGame::on_update(ice::EngineFrame& frame, ice::EngineRunner& runner, ice::WorldPortal& portal) noexcept -{ - using ice::operator""_sid_hash; - - runner.graphics_frame().enqueue_pass("default"_sid, "game.render"_sid, _game_gfx_pass.get()); - - static constexpr ice::String temp_ui_ids[]{ - "text_start", - "text_settings", - "text_credits", - "text_exit", - }; - static constexpr ice::String temp_ui_strings[]{ - "Start", - "Settings", - "Credits", - "Exit", - }; - - ice::shards::inspect_each( - runner.previous_frame().shards(), - ice::Shard_GameUI_Loaded, - [&frame, this](char const* page_name) noexcept - { - for (ice::u32 idx = 0; idx < ice::count(temp_ui_strings); ++idx) - { - ice::String* str = frame.storage().create_named_object(ice::stringid(temp_ui_strings[idx]), temp_ui_strings[idx]); - ice::UpdateUIResource* ures = frame.storage().create_named_object(ice::stringid(temp_ui_ids[idx])); - ures->page = page_name; - ures->resource_data = ice::to_const(str); - ures->resource = ice::stringid(temp_ui_ids[idx]); - ures->resource_type = ice::ui::ResourceType::String; - ice::shards::push_back(frame.shards(), ice::Shard_GameUI_UpdateResource | ice::to_const(ures)); - } - _menu = page_name; - } - ); - - if (frame.index() == 2) - { - ice::shards::push_back(frame.shards(), - ice::Shard_GameUI_Load | "ui/test" - ); - } - - bool was_active = _active; - for (ice::input::InputEvent const& event : frame.input_events()) - { - if (ice::input::input_identifier(ice::input::DeviceType::Keyboard, ice::input::KeyboardKey::Escape) == event.identifier) - { - if (_menu != nullptr && (event.value.button.state.clicked || event.value.button.state.repeat > 0)) - { - if (_menu_visible) - { - ice::shards::push_back(frame.shards(), ice::Shard_GameUI_Hide | _menu); - } - else - { - ice::shards::push_back(frame.shards(), ice::Shard_GameUI_Show | _menu); - } - _menu_visible = !_menu_visible; - } - } - if (ice::input::input_identifier(ice::input::DeviceType::Keyboard, ice::input::KeyboardKey::KeyP) == event.identifier) - { - if (event.value.button.state.clicked) - { - _active = !_active; - } - } - } - - _action_system->step_actions(frame.shards()); - - using ice::operator""_shardid; - for (ice::Shard const shard : frame.shards()) - { - if (shard == "event/player/can_jump_again"_shardid) - { - ICE_LOG(ice::LogSeverity::Debug, ice::LogTag::Game, "We can jump!"); - } - if (shard == ice::action::Shard_ActionEventSuccess) - { - ICE_LOG(ice::LogSeverity::Debug, ice::LogTag::Game, "Success!"); - } - if (shard == ice::action::Shard_ActionEventFailed) - { - ICE_LOG(ice::LogSeverity::Debug, ice::LogTag::Game, "Failure!"); - } - if (shard == ice::action::Shard_ActionEventReset) - { - ICE_LOG(ice::LogSeverity::Debug, ice::LogTag::Game, "Reset!"); - } - } - - ice::ecs::EntityHandle eh; - ice::Shard const player_entity_created = ice::shards::find_first_of(runner.previous_frame().shards(), ice::ecs::Shard_EntityCreated); - - if (ice::shard_inspect(player_entity_created, eh)) - { - //ICE_ASSERT(ice::ecs::entity_handle_info(eh).entity == ice::ecs::Entity{}, "{}", eh); - ICE_LOG(ice::LogSeverity::Debug, ice::LogTag::Game, "{}", eh); - } - - if (player_entity_created != ice::Shard_Invalid) - { - //ice::ecs::queue_remove_entity( - // frame.entity_operations(), - // ice::shard_shatter(player_entity_created) - //); - - ice::shards::push_back( - frame.shards(), - ice::Shard_SetDefaultCamera | "camera.default"_sid_hash - ); - } - - - if (was_active == true && _active == false) - { - ice::shards::push_back( - runner.current_frame().shards(), - ice::Shard_WorldDeactivate | _test_world - ); - } - else if (was_active == false && _active == true) - { - ice::shards::push_back( - runner.current_frame().shards(), - ice::Shard_WorldActivate | _test_world - ); - } -} -#endif diff --git a/source/code/test/private/game.hxx b/source/code/test/private/game.hxx index 2fb7eddb5..a4d0785a6 100644 --- a/source/code/test/private/game.hxx +++ b/source/code/test/private/game.hxx @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -30,7 +30,7 @@ public: private: ice::Allocator& _allocator; - ice::SnakeAllocator _tasks_alloc; + ice::ProxyAllocator _tasks_alloc; ice::UniquePtr _graph; ice::UniquePtr _graph_runtime; @@ -40,75 +40,3 @@ private: bool _first_time; }; - -#if 0 - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include -#include - -using ice::operator""_sid; -using ice::operator""_uri; - -class MyGame : public ice::WorldTrait -{ -public: - static constexpr ice::URI ConfigFile = "file://config.json"_uri; - - MyGame(ice::Allocator& alloc, ice::Clock const& clock) noexcept; - - auto graphics_world_template() const noexcept -> ice::WorldTemplate const&; - - void on_load_modules(ice::GameServices& sercies) noexcept; - void on_app_startup(ice::Engine& engine) noexcept; - void on_app_shutdown(ice::Engine& engine) noexcept; - - void on_game_begin(ice::EngineRunner& runner) noexcept; - void on_game_end() noexcept; - - void on_update( - ice::EngineFrame& frame, - ice::EngineRunner& runner, - ice::WorldPortal& portal - ) noexcept; - -public: - ice::ProxyAllocator _allocator; - ice::Clock const& _clock; - - ice::Engine* _current_engine; - - ice::ecs::ArchetypeIndex _ecs_archetypes; - ice::ecs::DataBlockPool _ecs_block_pool; - ice::UniquePtr _ecs_storage; - - ice::UniquePtr _game_gfx_pass; - - ice::UniquePtr _action_triggers{ }; - ice::UniquePtr _action_system{ }; - - bool _active = false; - ice::World* _test_world; - - char const* _menu = nullptr; - bool _menu_visible = false; -}; - -ICE_REGISTER_GAMEAPP(MyGame); - -#endif diff --git a/source/code/test/test.bff b/source/code/test/test.bff index b16b52827..bf013e197 100644 --- a/source/code/test/test.bff +++ b/source/code/test/test.bff @@ -6,7 +6,7 @@ .Name = 'test' .Kind = .Kind_ConsoleApp .Group = 'Tests' - .Requires = { 'Windows' } + .RequiresAny = { 'Windows', 'Linux' } .BaseDir = '$WorkspaceCodeDir$/test' @@ -32,12 +32,16 @@ .Requires = { 'Release' } .Deploy = true - .DependsOn = + + .Public = [ - .Runtime = { - 'shader_tools' - 'vulkan_renderer' - } + .DependsOn = + [ + .Runtime = { + 'shader_tools' + 'vulkan_renderer' + } + ] ] ] .Rules = { .ReleaseDeploy } diff --git a/source/code/tools/asset_compiler/asset_compiler.bff b/source/code/tools/asset_compiler/asset_compiler.bff index a41fcae37..829e6c4aa 100644 --- a/source/code/tools/asset_compiler/asset_compiler.bff +++ b/source/code/tools/asset_compiler/asset_compiler.bff @@ -10,10 +10,6 @@ .CopyModules = true - .Requires = { - 'Windows' - } - .LocalCompiler = [ .Name = 'isac' diff --git a/source/code/tools/asset_compiler/private/asset_compiler_app.cxx b/source/code/tools/asset_compiler/private/asset_compiler_app.cxx index 6a70f3210..5e0373f39 100644 --- a/source/code/tools/asset_compiler/private/asset_compiler_app.cxx +++ b/source/code/tools/asset_compiler/private/asset_compiler_app.cxx @@ -1,8 +1,8 @@ /// Copyright 2024 - 2025, Dandielo /// SPDX-License-Identifier: MIT +#include #include -#include #include #include #include @@ -15,6 +15,7 @@ #include #include #include +#include #include "asset_compiler_resource_provider.hxx" @@ -28,9 +29,9 @@ class AssetCompilerApp : public ice::tool::ToolApp , _output{ } , _asset_resource{ } , _includes{ _allocator } - , _params{ _allocator } , _inputs_meta{ _allocator } , _inputs{ _allocator } + , _params{ _allocator } , _output_raw{ false } , _output_std{ false } , _queue{ } @@ -159,6 +160,11 @@ class AssetCompilerApp : public ice::tool::ToolApp { .predicted_resource_count = 10'000, .io_dedicated_threads = 0 } ); + if (ice::string::empty(_asset_basepath)) + { + _asset_basepath = ice::app::workingdir(); + } + ice::ResourceFileEntry const file_list[]{ {.path = _inputs[0], .basepath = _asset_basepath} }; ice::ResourceProvider* const file_provider = resource_tracker->attach_provider( ice::create_resource_provider_files(_allocator, file_list, nullptr, "") @@ -254,14 +260,14 @@ class AssetCompilerApp : public ice::tool::ToolApp { if (resource_compiler == nullptr || resource_compiler->fn_supported_resources == nullptr) { - ICE_LOG(ice::LogSeverity::Critical, ice::LogTag::Tool, "Resource compiler for resource {} is not available.", _asset_resource); + ICE_LOG(ice::LogSeverity::Critical, ice::LogTag::Tool, "Resource compiler for resource '{}' is not available.", res->name()); return 1; } ice::ucount out_idx = 0; if (ice::search(resource_compiler->fn_supported_resources(_params), res_ext, out_idx) == false) { - ICE_LOG(ice::LogSeverity::Critical, ice::LogTag::Tool, "Resource compiler for resource {} is not available.", _asset_resource); + ICE_LOG(ice::LogSeverity::Critical, ice::LogTag::Tool, "Resource compiler for resource '{}' is not available.", res->name()); return 1; } @@ -463,4 +469,7 @@ class AssetCompilerApp : public ice::tool::ToolApp ice::TaskQueue _queue; ice::TaskScheduler _scheduler; ice::UniquePtr _thread; + + // Workaround for Clang + static inline ice::tool::ToolAppInstancer const& _workaroundSymbol = AssetCompilerApp::AppInstancer; }; diff --git a/source/code/tools/asset_compiler/private/asset_compiler_resource_provider.cxx b/source/code/tools/asset_compiler/private/asset_compiler_resource_provider.cxx index 07d9a8eb5..7527a88e1 100644 --- a/source/code/tools/asset_compiler/private/asset_compiler_resource_provider.cxx +++ b/source/code/tools/asset_compiler/private/asset_compiler_resource_provider.cxx @@ -20,7 +20,6 @@ AssetCompilerResource::AssetCompilerResource( ice::native_file::path_from_string(metapath, _path); ice::string::push_back(metapath, ISP_PATH_LITERAL(".isrm")); - ice::Data result{}; if (auto metafile = ice::native_file::open_file(metapath, ice::native_file::FileOpenFlags::Read); file) { _metadata = _allocator.allocate(ice::native_file::sizeof_file(metafile)); diff --git a/source/code/tools/hsc_packer/hsc_packer.bff b/source/code/tools/hsc_packer/hsc_packer.bff index 23b4a7225..8e6eb6d79 100644 --- a/source/code/tools/hsc_packer/hsc_packer.bff +++ b/source/code/tools/hsc_packer/hsc_packer.bff @@ -18,10 +18,6 @@ .LocalCompilerConfig = 'Release' ] - .Requires = { - 'Windows' - } - .Private = [ .Modules = { diff --git a/source/code/tools/hsc_packer/private/hsc_packer.cxx b/source/code/tools/hsc_packer/private/hsc_packer.cxx index 931b62ff1..23cefb4bc 100644 --- a/source/code/tools/hsc_packer/private/hsc_packer.cxx +++ b/source/code/tools/hsc_packer/private/hsc_packer.cxx @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -321,7 +322,8 @@ class HailStormPackerApp final : public ice::tool::ToolApp // the event is signalled which means all required resources where loaded. while (res_count.load(std::memory_order_relaxed) != res_idx) { - SleepEx(1, 0); + using ice::operator""_Tms; + ice::current_thread::sleep(1_Tms); } ice::array::resize(resource_paths, res_idx); @@ -400,4 +402,7 @@ class HailStormPackerApp final : public ice::tool::ToolApp // Filters ice::Array> _filter_extensions_heap; ice::Array _filter_extensions; + + // Workaround for Clang + static inline ice::tool::ToolAppInstancer const& _workaroundSymbol = HailStormPackerApp::AppInstancer; }; diff --git a/source/code/tools/hsc_packer/private/hsc_packer_aiostream.cxx b/source/code/tools/hsc_packer/private/hsc_packer_aiostream.cxx index 7e01faa98..75f8329a0 100644 --- a/source/code/tools/hsc_packer/private/hsc_packer_aiostream.cxx +++ b/source/code/tools/hsc_packer/private/hsc_packer_aiostream.cxx @@ -6,6 +6,7 @@ #include #include +#include #include using ice::LogSeverity; @@ -41,6 +42,7 @@ struct HailstormAIOWriter ice::Span _resources; std::atomic_uint32_t _started_writes; + std::atomic_uint32_t _finished_loads; std::atomic_uint32_t _finished_writes; ~HailstormAIOWriter() noexcept = default; @@ -100,7 +102,7 @@ inline auto HailstormAIOWriter::async_write(ice::usize write_offset, hailstorm:: inline bool await_ready() const noexcept { return false; } inline bool await_suspend(std::coroutine_handle<> coro_handle) noexcept { - ICE_ASSERT_CORE(_data.size <= MAXDWORD); + ICE_ASSERT_CORE(_data.size <= std::numeric_limits::max()); IPT_ZONE_SCOPED_NAMED("AsyncStream::async_write"); // Need to set the coroutine before calling Write, since we could already be finishing writing on a different thread @@ -216,10 +218,16 @@ inline bool HailstormAIOWriter::open_and_resize(ice::usize total_size) noexcept using enum ice::native_file::FileOpenFlags; _file = ice::native_file::open_file(_aioport, _filepath, Write | Exclusive | Asynchronous).value(); +#if ISP_WINDOWS // Resize the file SetFilePointerEx(_file.native(), { .QuadPart = ice::isize(total_size).value }, NULL, FILE_BEGIN); SetEndOfFile(_file.native()); SetFilePointerEx(_file.native(), { 0 }, NULL, FILE_BEGIN); +#elif ISP_LINUX + ftruncate64(_file.native(), total_size.value); +#else + ICE_ASSER_CORE(false); +#endif // Create the completion port return true; @@ -250,7 +258,8 @@ inline bool HailstormAIOWriter::close() noexcept { while (_finished_writes != _started_writes) { - SleepEx(5, FALSE); + using ice::operator""_Tms; + ice::current_thread::sleep(5_Tms); } return true; @@ -281,6 +290,7 @@ inline auto HailstormAIOWriter::async_write_resource(ice::u32 idx, ice::usize of { _started_writes.fetch_add(1, std::memory_order_relaxed); ice::ResourceResult const load_result = co_await _resource_tracker.load_resource(_resources[idx]); + _finished_loads.fetch_add(1, std::memory_order_relaxed); if (load_result.resource_status == ice::ResourceStatus::Loaded) { bool const success = co_await async_write(offset, data_to_hsdata(load_result.data)); diff --git a/source/code/tools/hsc_packer/private/hsc_packer_app.cxx b/source/code/tools/hsc_packer/private/hsc_packer_app.cxx index 5b20e9bf5..3c756c1ea 100644 --- a/source/code/tools/hsc_packer/private/hsc_packer_app.cxx +++ b/source/code/tools/hsc_packer/private/hsc_packer_app.cxx @@ -2,7 +2,7 @@ /// SPDX-License-Identifier: MIT #include "hsc_packer_app.hxx" -#include +#include #include #include #include @@ -13,9 +13,8 @@ auto hscp_process_directory(ice::Allocator& alloc, ice::String dir) noexcept -> ice::HeapString<> searched_utf8_path{ alloc, dir }; if (ice::path::is_absolute(dir) == false) { - ice::string::clear(searched_utf8_path); - ice::wide_to_utf8_append(ice::tool::path_current_directory(), searched_utf8_path); - ice::string::push_back(searched_utf8_path, dir); + searched_utf8_path = ice::app::workingdir(); + ice::path::join(searched_utf8_path, dir); } ice::path::normalize(searched_utf8_path); diff --git a/source/code/tools/hsc_reader/hsc_reader.bff b/source/code/tools/hsc_reader/hsc_reader.bff index 17de567dc..bbef5d5ec 100644 --- a/source/code/tools/hsc_reader/hsc_reader.bff +++ b/source/code/tools/hsc_reader/hsc_reader.bff @@ -1,4 +1,4 @@ -/// Copyright 2023 - 2024, Dandielo +/// Copyright 2023 - 2025, Dandielo /// SPDX-License-Identifier: MIT .Project = @@ -10,10 +10,6 @@ .CopyModules = true - .Requires = { - 'Windows' - } - .Private = [ .Modules = { diff --git a/source/code/tools/hsc_reader/private/hsc_reader.cxx b/source/code/tools/hsc_reader/private/hsc_reader.cxx index 19959e879..c64c11789 100644 --- a/source/code/tools/hsc_reader/private/hsc_reader.cxx +++ b/source/code/tools/hsc_reader/private/hsc_reader.cxx @@ -47,7 +47,7 @@ class HailStormReaderApp final : public ice::tool::ToolApp // Open the pack file if (packfile_validate() == false) { - ICE_LOG(ice::LogSeverity::Retail, LogTag_Main, "Provide input file is not a valid Hailstorm pack."); + ICE_LOG(ice::LogSeverity::Retail, LogTag_Main, "Provided input file is not a valid Hailstorm pack."); return 1; } @@ -67,7 +67,7 @@ class HailStormReaderApp final : public ice::tool::ToolApp _file_path = ice::tool::path_make_absolute(_file_path); if (_file = ice::native_file::open_file(_file_path); _file) { - ice::usize const bytes_read = ice::native_file::read_file( + ice::native_file::read_file( _file, ice::size_of, { &_data.header, ice::size_of, ice::align_of } @@ -125,4 +125,7 @@ class HailStormReaderApp final : public ice::tool::ToolApp ice::native_file::HeapFilePath _file_path; ice::native_file::File _file; hailstorm::HailstormData _data; + + // Workaround for Clang + static inline ice::tool::ToolAppInstancer const& _workaroundSymbol = HailStormReaderApp::AppInstancer; }; diff --git a/source/code/tools/hsc_reader/private/hsc_reader_app.cxx b/source/code/tools/hsc_reader/private/hsc_reader_app.cxx index 506032a15..8e1e110fc 100644 --- a/source/code/tools/hsc_reader/private/hsc_reader_app.cxx +++ b/source/code/tools/hsc_reader/private/hsc_reader_app.cxx @@ -29,11 +29,13 @@ bool ParamRange::param_parse_results(ParamRange& range, ice::Span tags[]{ - { LogTag_Main, &Param_HideHeader.value }, - { LogTag_InfoChunks, &Param_ShowChunks.value.set }, - { LogTag_InfoResources, &Param_ShowResources.value.set }, - { LogTag_InfoPaths, &Param_ShowResourcePaths.value }, + ice::log_tag_register(LogTag_Main); + + static const std::tuple tags[]{ + { LogTag_InfoHeader, Param_HideHeader.value == false }, + { LogTag_InfoChunks, Param_ShowChunks.value.set }, + { LogTag_InfoResources, Param_ShowResources.value.set }, + { LogTag_InfoPaths, Param_ShowResourcePaths.value }, }; for (auto const& tag : tags) @@ -41,7 +43,6 @@ void hscr_initialize_logging() noexcept ice::log_tag_register(std::get<0>(tag)); // Check if the tag can be enabled. - bool* temp_param = std::get<1>(tag); - ice::log_tag_enable(std::get<0>(tag).tag, *temp_param); + ice::log_tag_enable(std::get<0>(tag).tag, std::get<1>(tag)); } } diff --git a/source/code/tools/hsc_reader/private/hsc_reader_app.hxx b/source/code/tools/hsc_reader/private/hsc_reader_app.hxx index 9fc18a4da..ed9bf7fa3 100644 --- a/source/code/tools/hsc_reader/private/hsc_reader_app.hxx +++ b/source/code/tools/hsc_reader/private/hsc_reader_app.hxx @@ -50,5 +50,5 @@ static ice::ParamInstance Param_ShowCustomValues{ "", "--custom-vals", "Shows app custom values (if the format supports them)." }; static ice::ParamInstance Param_HideHeader{ - "", "--hide,--header", "Shows header information." + "", "--hide", "Hides header information.", }; diff --git a/source/code/tools/hsc_reader/private/hsc_reader_funcs.cxx b/source/code/tools/hsc_reader/private/hsc_reader_funcs.cxx index 56eb96aeb..664e222b2 100644 --- a/source/code/tools/hsc_reader/private/hsc_reader_funcs.cxx +++ b/source/code/tools/hsc_reader/private/hsc_reader_funcs.cxx @@ -42,6 +42,7 @@ void hailstorm_print_headerinfo( (Constant_HailstormHeaderVersion_0 & 0x00ff'0000) >> 16, (Constant_HailstormHeaderVersion_0 & 0x0000'ff00) >> 8, (Constant_HailstormHeaderVersion_0 & 0x0000'00ff) >> 0, + 0 }; HSCR_INFO(LogTag_InfoHeader, "Base-Header"); diff --git a/source/code/tools/tool_base/private/tool.cxx b/source/code/tools/tool_base/private/tool.cxx index 4b0c98426..bd790ab40 100644 --- a/source/code/tools/tool_base/private/tool.cxx +++ b/source/code/tools/tool_base/private/tool.cxx @@ -2,6 +2,7 @@ /// SPDX-License-Identifier: MIT #include +#include #include #include #include @@ -16,12 +17,8 @@ auto ice::tool::global_allocator() noexcept -> ice::Allocator& auto ice::tool::path_current_directory() noexcept -> ice::native_file::HeapFilePath { - ice::u32 const dirsize = GetCurrentDirectoryW(0, 0) - 1; // We don't need the '0' null count ice::native_file::HeapFilePath current_workingdir{ global_allocator() }; - ice::string::resize(current_workingdir, dirsize); - GetCurrentDirectoryW(current_workingdir._capacity, current_workingdir._data); - ice::string::push_back(current_workingdir, L'\\'); - ice::path::normalize(current_workingdir); + ice::native_file::path_from_string(current_workingdir, ice::app::workingdir()); return current_workingdir; } diff --git a/source/code/tools/tool_base/tool_base.bff b/source/code/tools/tool_base/tool_base.bff index 775b63c08..4fa0c5ca8 100644 --- a/source/code/tools/tool_base/tool_base.bff +++ b/source/code/tools/tool_base/tool_base.bff @@ -8,15 +8,13 @@ .BaseDir = '$WorkspaceCodeDir$/tools/tool_base' .Group = 'Tools' + .RequiresAny = { 'Windows', 'Linux' } .CopyModules = true - .Requires = { - 'Windows' - } - - .Private = + .Public = [ .Uses = { + 'application' 'resource_system' } ] diff --git a/source/conanfile.txt b/source/conanfile.txt index ad64222af..4a8e7bbb1 100644 --- a/source/conanfile.txt +++ b/source/conanfile.txt @@ -6,7 +6,7 @@ fastbuild-generator/0.4.2@iceshard/stable [requires] cli11/2.4.1@iceshard/stable -fmt/10.0.0@iceshard/stable +fmt/11.2.0@iceshard/stable tracy/0.11.1@iceshard/stable imgui/1.90.docking@iceshard/stable hailstorm/0.4.0@iceshard/stable @@ -15,11 +15,12 @@ arctic/0.2.2@iceshard/stable rapidxml/1.13 rapidxml_ns/1.13.2@iceshard/stable -rapidjson/1.1.0 +rapidjson/1.1.0.patched@iceshard/stable rapidfuzz_cpp/3.0.5@iceshard/stable -[requires-Windows-MSVC] vulkan-memory-allocator/3.0.1 + +[requires-Windows-MSVC] catch2/3.3.2@iceshard/stable assimp/5.2.5@iceshard/stable @@ -32,11 +33,19 @@ msdf_atlas_gen/1.2.2@iceshard/stable # Editor only packages imguizmo/1.91.3@iceshard/stable -[requires-Android-Arm64] -vulkan-memory-allocator/3.0.1 +[requires-Linux-Clang] +catch2/3.3.2@iceshard/stable + +assimp/5.2.5@iceshard/stable +sdl2/2.0.22@iceshard/stable + +freetype/2.12.1@iceshard/stable +msdfgen/1.9.2@iceshard/stable +msdf_atlas_gen/1.2.2@iceshard/stable + +# Editor only packages +imguizmo/1.91.3@iceshard/stable -[requires-Android-x64] -vulkan-memory-allocator/3.0.1 [options-Android-Arm64] tracy/*:shared=False diff --git a/source/conanprofiles.txt b/source/conanprofiles.txt index 173a9791c..41293fdfe 100644 --- a/source/conanprofiles.txt +++ b/source/conanprofiles.txt @@ -13,6 +13,7 @@ os=Linux arch=x86_64 compiler=clang compiler.cppstd=20 +compiler.libcxx=libc++ compiler.version=$(ConanCompilerVersion) diff --git a/source/configs.bff b/source/configs.bff index 63686337c..8322d0f5a 100644 --- a/source/configs.bff +++ b/source/configs.bff @@ -1,3 +1,5 @@ +/// Copyright 2022 - 2025, Dandielo +/// SPDX-License-Identifier: MIT .Configuration_Debug = [ @@ -92,6 +94,7 @@ '-Wno-error=deprecated-declarations' '-Wno-switch' '-std=c++23' + '-stdlib=libc++' } ] @@ -110,11 +113,18 @@ .Name = 'Config-Unix' .Requires = { 'Unix', 'CLANG-Frontend' } .BuildOptions = { + '-fPIC' // Some warnigns disabled for now. To be fixed later. '-Wno-unused-command-line-argument' '-Wno-unused-private-field' '-Wno-deprecated-declarations' '-Wno-missing-braces' + // '-fsanitize=address' + } + .LinkLinkerOptions = { + // Because we want to select libraries stored besides executables we need to set the -rpath + '-Wl,-rpath,^$ORIGIN/' + // '-fsanitize=address' } ] @@ -122,11 +132,13 @@ .Config_Unix_BinaryBase = [ .Name = 'Config-Unix-Binary' - .Requires = { 'Unix', 'GCC-Frontend' } + .Requires = { 'Unix', 'CLANG-Frontend' } .LinkOptions = { '-lc' '-lm' - '-lstdc++' + '-ldl' + '-lpthread' + '-lc++' } ] diff --git a/source/data/shaders/shaders.bff b/source/data/shaders/shaders.bff index df821fde6..a6d3e7737 100644 --- a/source/data/shaders/shaders.bff +++ b/source/data/shaders/shaders.bff @@ -1,4 +1,4 @@ -/// Copyright 2022 - 2022, Dandielo +/// Copyright 2022 - 2025, Dandielo /// SPDX-License-Identifier: MIT .Project = diff --git a/source/fbuild.bff b/source/fbuild.bff index 5e98345eb..be2415e52 100644 --- a/source/fbuild.bff +++ b/source/fbuild.bff @@ -1,19 +1,11 @@ -#if __WINDOWS__ +/// Copyright 2022 - 2025, Dandielo +/// SPDX-License-Identifier: MIT -#import SystemRoot Settings { - .Environment = - { - "SystemRoot=$SystemRoot$" - "TMP=$SystemRoot$\temp" - // TODO: Webasm, find a wat not hardcode this variable - "EMSDK_PYTHON=$WorkspaceRoot$/build/webasm/python/3.9.2-nuget_64bit/python.exe" - } + // Custom settings if necessary, the '.Environment' variable is already set } -#endif - #include "configs.bff" ^SolutionItems = { @@ -46,30 +38,18 @@ Settings ] .PipelineConanProfile = 'Windows-MSVC' - - // Adding custom conan profile generator variables - .ConanProfileVariables + 'MSVCCompilerVersion' - .MSVCCompilerVersion = '194' -] - -.Unity_Pipeline_Linux_x64 = -[ - Using( .Pipeline_Linux_x64 ) - - .PipelineAllowUnityBuilds = true - .PipelineSteps = { 'Build' } // Linking still doesn't work - - .PipelineConanProfile = 'Linux-GCC' ] .Unity_Pipeline_Linux_x64_Clang = [ Using( .Pipeline_Linux_x64 ) - .PipelineName = 'x64-Clang' - .PipelineToolchain = 'clang-14.0.0' + .PipelineName = 'x64' + .PipelineToolchain = 'clang-20.0.0' .PipelineAllowUnityBuilds = true - .PipelineSteps = { 'Build' } // Linking still doesn't work + + // Projects from this pipeline may be exposed as compilers for other pipelines + .PipelineDefinesCompilers = true .PipelineConanProfile = 'Linux-Clang' ] @@ -165,7 +145,6 @@ Settings ^BuildPipelines = { .Unity_Pipeline_Windows_x64 - .Unity_Pipeline_Linux_x64 .Unity_Pipeline_Linux_x64_Clang .Unity_Pipeline_Android_ARM64 .Unity_Pipeline_Android_x64 diff --git a/thirdparty/LICENSES.txt b/thirdparty/LICENSES.txt index 1608fe6e8..ffe758822 100644 --- a/thirdparty/LICENSES.txt +++ b/thirdparty/LICENSES.txt @@ -82,29 +82,29 @@ -------------------- START 'catch2' -------------------- - Boost Software License - Version 1.0 - August 17th, 2003 - - Permission is hereby granted, free of charge, to any person or organization - obtaining a copy of the software and accompanying documentation covered by - this license (the "Software") to use, reproduce, display, distribute, - execute, and transmit the Software, and to prepare derivative works of the - Software, and to permit third-parties to whom the Software is furnished to - do so, all subject to the following: - - The copyright notices in the Software and this entire statement, including - the above license grant, this restriction and the following disclaimer, - must be included in all copies of the Software, in whole or in part, and - all derivative works of the Software, unless such copies or derivative - works are solely in the form of machine-executable object code generated by - a source language processor. - - 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT - SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. + Boost Software License - Version 1.0 - August 17th, 2003 + + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to + do so, all subject to the following: + + The copyright notices in the Software and this entire statement, including + the above license grant, this restriction and the following disclaimer, + must be included in all copies of the Software, in whole or in part, and + all derivative works of the Software, unless such copies or derivative + works are solely in the form of machine-executable object code generated by + a source language processor. + + 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. -------------------- END 'catch2' -------------------- @@ -138,206 +138,206 @@ -------------------- START 'fmt' -------------------- - Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors - - 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. - - --- Optional exception to the license --- - - As an exception, if, as a result of your compiling your source code, portions - of this Software are embedded into a machine-executable object form of such - source code, you may redistribute such embedded portions in such object form - without including the above copyright and permission notices. + Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors + + 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. + + --- Optional exception to the license --- + + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into a machine-executable object form of such + source code, you may redistribute such embedded portions in such object form + without including the above copyright and permission notices. -------------------- END 'fmt' -------------------- -------------------- START 'freetype' -------------------- - The FreeType Project LICENSE - ---------------------------- - - 2006-Jan-27 - - Copyright 1996-2002, 2006 by - David Turner, Robert Wilhelm, and Werner Lemberg - - - - Introduction - ============ - - The FreeType Project is distributed in several archive packages; - some of them may contain, in addition to the FreeType font engine, - various tools and contributions which rely on, or relate to, the - FreeType Project. - - This license applies to all files found in such packages, and - which do not fall under their own explicit license. The license - affects thus the FreeType font engine, the test programs, - documentation and makefiles, at the very least. - - This license was inspired by the BSD, Artistic, and IJG - (Independent JPEG Group) licenses, which all encourage inclusion - and use of free software in commercial and freeware products - alike. As a consequence, its main points are that: - - o We don't promise that this software works. However, we will be - interested in any kind of bug reports. (`as is' distribution) - - o You can use this software for whatever you want, in parts or - full form, without having to pay us. (`royalty-free' usage) - - o You may not pretend that you wrote this software. If you use - it, or only parts of it, in a program, you must acknowledge - somewhere in your documentation that you have used the - FreeType code. (`credits') - - We specifically permit and encourage the inclusion of this - software, with or without modifications, in commercial products. - We disclaim all warranties covering The FreeType Project and - assume no liability related to The FreeType Project. - - - Finally, many people asked us for a preferred form for a - credit/disclaimer to use in compliance with this license. We thus - encourage you to use the following text: - - """ - Portions of this software are copyright © The FreeType - Project (www.freetype.org). All rights reserved. - """ - - Please replace with the value from the FreeType version you - actually use. - - - Legal Terms - =========== - - 0. Definitions - -------------- - - Throughout this license, the terms `package', `FreeType Project', - and `FreeType archive' refer to the set of files originally - distributed by the authors (David Turner, Robert Wilhelm, and - Werner Lemberg) as the `FreeType Project', be they named as alpha, - beta or final release. - - `You' refers to the licensee, or person using the project, where - `using' is a generic term including compiling the project's source - code as well as linking it to form a `program' or `executable'. - This program is referred to as `a program using the FreeType - engine'. - - This license applies to all files distributed in the original - FreeType Project, including all source code, binaries and - documentation, unless otherwise stated in the file in its - original, unmodified form as distributed in the original archive. - If you are unsure whether or not a particular file is covered by - this license, you must contact us to verify this. - - The FreeType Project is copyright (C) 1996-2000 by David Turner, - Robert Wilhelm, and Werner Lemberg. All rights reserved except as - specified below. - - 1. No Warranty - -------------- - - THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY - KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO - USE, OF THE FREETYPE PROJECT. - - 2. Redistribution - ----------------- - - This license grants a worldwide, royalty-free, perpetual and - irrevocable right and license to use, execute, perform, compile, - display, copy, create derivative works of, distribute and - sublicense the FreeType Project (in both source and object code - forms) and derivative works thereof for any purpose; and to - authorize others to exercise some or all of the rights granted - herein, subject to the following conditions: - - o Redistribution of source code must retain this license file - (`FTL.TXT') unaltered; any additions, deletions or changes to - the original files must be clearly indicated in accompanying - documentation. The copyright notices of the unaltered, - original files must be preserved in all copies of source - files. - - o Redistribution in binary form must provide a disclaimer that - states that the software is based in part of the work of the - FreeType Team, in the distribution documentation. We also - encourage you to put an URL to the FreeType web page in your - documentation, though this isn't mandatory. - - These conditions apply to any software derived from or based on - the FreeType Project, not just the unmodified files. If you use - our work, you must acknowledge us. However, no fee need be paid - to us. - - 3. Advertising - -------------- - - Neither the FreeType authors and contributors nor you shall use - the name of the other for commercial, advertising, or promotional - purposes without specific prior written permission. - - We suggest, but do not require, that you use one or more of the - following phrases to refer to this software in your documentation - or advertising materials: `FreeType Project', `FreeType Engine', - `FreeType library', or `FreeType Distribution'. - - As you have not signed this license, you are not required to - accept it. However, as the FreeType Project is copyrighted - material, only this license, or another one contracted with the - authors, grants you the right to use, distribute, and modify it. - Therefore, by using, distributing, or modifying the FreeType - Project, you indicate that you understand and accept all the terms - of this license. - - 4. Contacts - ----------- - - There are two mailing lists related to FreeType: - - o freetype@nongnu.org - - Discusses general use and applications of FreeType, as well as - future and wanted additions to the library and distribution. - If you are looking for support, start in this list if you - haven't found anything to help you in the documentation. - - o freetype-devel@nongnu.org - - Discusses bugs, as well as engine internals, design issues, - specific licenses, porting, etc. - - Our home page can be found at - - https://www.freetype.org - - - --- end of FTL.TXT --- + The FreeType Project LICENSE + ---------------------------- + + 2006-Jan-27 + + Copyright 1996-2002, 2006 by + David Turner, Robert Wilhelm, and Werner Lemberg + + + + Introduction + ============ + + The FreeType Project is distributed in several archive packages; + some of them may contain, in addition to the FreeType font engine, + various tools and contributions which rely on, or relate to, the + FreeType Project. + + This license applies to all files found in such packages, and + which do not fall under their own explicit license. The license + affects thus the FreeType font engine, the test programs, + documentation and makefiles, at the very least. + + This license was inspired by the BSD, Artistic, and IJG + (Independent JPEG Group) licenses, which all encourage inclusion + and use of free software in commercial and freeware products + alike. As a consequence, its main points are that: + + o We don't promise that this software works. However, we will be + interested in any kind of bug reports. (`as is' distribution) + + o You can use this software for whatever you want, in parts or + full form, without having to pay us. (`royalty-free' usage) + + o You may not pretend that you wrote this software. If you use + it, or only parts of it, in a program, you must acknowledge + somewhere in your documentation that you have used the + FreeType code. (`credits') + + We specifically permit and encourage the inclusion of this + software, with or without modifications, in commercial products. + We disclaim all warranties covering The FreeType Project and + assume no liability related to The FreeType Project. + + + Finally, many people asked us for a preferred form for a + credit/disclaimer to use in compliance with this license. We thus + encourage you to use the following text: + + """ + Portions of this software are copyright © The FreeType + Project (www.freetype.org). All rights reserved. + """ + + Please replace with the value from the FreeType version you + actually use. + + + Legal Terms + =========== + + 0. Definitions + -------------- + + Throughout this license, the terms `package', `FreeType Project', + and `FreeType archive' refer to the set of files originally + distributed by the authors (David Turner, Robert Wilhelm, and + Werner Lemberg) as the `FreeType Project', be they named as alpha, + beta or final release. + + `You' refers to the licensee, or person using the project, where + `using' is a generic term including compiling the project's source + code as well as linking it to form a `program' or `executable'. + This program is referred to as `a program using the FreeType + engine'. + + This license applies to all files distributed in the original + FreeType Project, including all source code, binaries and + documentation, unless otherwise stated in the file in its + original, unmodified form as distributed in the original archive. + If you are unsure whether or not a particular file is covered by + this license, you must contact us to verify this. + + The FreeType Project is copyright (C) 1996-2000 by David Turner, + Robert Wilhelm, and Werner Lemberg. All rights reserved except as + specified below. + + 1. No Warranty + -------------- + + THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO + USE, OF THE FREETYPE PROJECT. + + 2. Redistribution + ----------------- + + This license grants a worldwide, royalty-free, perpetual and + irrevocable right and license to use, execute, perform, compile, + display, copy, create derivative works of, distribute and + sublicense the FreeType Project (in both source and object code + forms) and derivative works thereof for any purpose; and to + authorize others to exercise some or all of the rights granted + herein, subject to the following conditions: + + o Redistribution of source code must retain this license file + (`FTL.TXT') unaltered; any additions, deletions or changes to + the original files must be clearly indicated in accompanying + documentation. The copyright notices of the unaltered, + original files must be preserved in all copies of source + files. + + o Redistribution in binary form must provide a disclaimer that + states that the software is based in part of the work of the + FreeType Team, in the distribution documentation. We also + encourage you to put an URL to the FreeType web page in your + documentation, though this isn't mandatory. + + These conditions apply to any software derived from or based on + the FreeType Project, not just the unmodified files. If you use + our work, you must acknowledge us. However, no fee need be paid + to us. + + 3. Advertising + -------------- + + Neither the FreeType authors and contributors nor you shall use + the name of the other for commercial, advertising, or promotional + purposes without specific prior written permission. + + We suggest, but do not require, that you use one or more of the + following phrases to refer to this software in your documentation + or advertising materials: `FreeType Project', `FreeType Engine', + `FreeType library', or `FreeType Distribution'. + + As you have not signed this license, you are not required to + accept it. However, as the FreeType Project is copyrighted + material, only this license, or another one contracted with the + authors, grants you the right to use, distribute, and modify it. + Therefore, by using, distributing, or modifying the FreeType + Project, you indicate that you understand and accept all the terms + of this license. + + 4. Contacts + ----------- + + There are two mailing lists related to FreeType: + + o freetype@nongnu.org + + Discusses general use and applications of FreeType, as well as + future and wanted additions to the library and distribution. + If you are looking for support, start in this list if you + haven't found anything to help you in the documentation. + + o freetype-devel@nongnu.org + + Discusses bugs, as well as engine internals, design issues, + specific licenses, porting, etc. + + Our home page can be found at + + https://www.freetype.org + + + --- end of FTL.TXT --- -------------------- END 'freetype' -------------------- @@ -367,102 +367,102 @@ -------------------- START 'imgui' -------------------- - The MIT License (MIT) - - Copyright (c) 2014-2023 Omar Cornut - - 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. + The MIT License (MIT) + + Copyright (c) 2014-2023 Omar Cornut + + 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. -------------------- END 'imgui' -------------------- -------------------- START 'imguizmo' -------------------- - The MIT License (MIT) - - Copyright (c) 2016 Cedric Guillemet - - 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. + The MIT License (MIT) + + Copyright (c) 2016 Cedric Guillemet + + 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. -------------------- END 'imguizmo' -------------------- -------------------- START 'msdf_atlas_gen' -------------------- - MIT License - - Copyright (c) 2020 Viktor Chlumsky - - 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. + MIT License + + Copyright (c) 2020 Viktor Chlumsky + + 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. -------------------- END 'msdf_atlas_gen' -------------------- -------------------- START 'msdfgen' -------------------- - MIT License - - Copyright (c) 2016 Viktor Chlumsky - - 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. + MIT License + + Copyright (c) 2016 Viktor Chlumsky + + 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. -------------------- END 'msdfgen' -------------------- @@ -687,33 +687,33 @@ -------------------- START 'tracy' -------------------- - Tracy Profiler (https://github.com/wolfpld/tracy) is licensed under the - 3-clause BSD license. - - Copyright (c) 2017-2024, Bartosz Taudul - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Tracy Profiler (https://github.com/wolfpld/tracy) is licensed under the + 3-clause BSD license. + + Copyright (c) 2017-2024, Bartosz Taudul + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------- END 'tracy' -------------------- diff --git a/thirdparty/README.md b/thirdparty/README.md index 6a4fe3bb0..a267108ed 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -25,7 +25,7 @@ CLI11 is a command line parser for C++11 and beyond that provides a rich feature ## fmt {{fmt}} is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams. - **upstream:** https://fmt.dev/latest/index.html -- **version:** 10.0.0 +- **version:** 11.2.0 - **license:** MIT ## freetype @@ -45,7 +45,7 @@ Custom package format for storing resources used by 'iceshard' game engine frame ## imgui Dear ImGui: Bloat-free Immediate Mode Graphical User interface for C++ with minimal dependencies - **upstream:** https://github.com/ocornut/imgui -- **version:** 1.90 +- **version:** 1.90.docking - **license:** MIT ## imguizmo @@ -75,7 +75,7 @@ Rapid fuzzy string matching in C++ using the Levenshtein Distance ## rapidjson A fast JSON parser/generator for C++ with both SAX/DOM style API - **upstream:** https://github.com/Tencent/rapidjson -- **version:** 1.1.0 +- **version:** 1.1.0.patched - **license:** MIT ## rapidxml diff --git a/tools/iceshard.moon b/tools/iceshard.moon index 8c403e93d..e1c7d52e4 100644 --- a/tools/iceshard.moon +++ b/tools/iceshard.moon @@ -9,6 +9,7 @@ import ExecCommand from require 'ice.commands.exec' import AndroidCommand from require 'ice.commands.android' import WebAsmCommand from require 'ice.commands.webasm' import SettingsCommand from require 'ice.commands.settings' +import SDKCommand from require 'ice.commands.sdk' import RunCommand from require 'tools.run' import NatvisCommand from require 'tools.natvis' @@ -31,6 +32,7 @@ class IceShard extends Application 'android': AndroidCommand 'webasm': WebAsmCommand 'settings': SettingsCommand + 'sdk': SDKCommand -- Custom commands 'natvis': NatvisCommand 'doxy': DoxyCommand diff --git a/tools/scripts/start.moon b/tools/scripts/start.moon index 86e83b8b0..39f674282 100644 --- a/tools/scripts/start.moon +++ b/tools/scripts/start.moon @@ -5,7 +5,28 @@ config = arg[1] or 'Develop' arch = arg[2] or 'x64' app = arg[3] or 'test' +build_dir = Path\normalize Path\join Dir\current!, '../../build/bin' +unless Dir\exists build_dir then error "Binaries directory '#{build_dir}' does not exist" + +pipeline_dir = nil +for name in Dir\list build_dir + if name\match arch + pipeline_dir = Path\join build_dir, name + break + +unless pipeline_dir then error "Failed to find matching pipeline for arch = #{arch}" + +config_dir = nil +for name in Dir\list pipeline_dir + if name\match config + config_dir = Path\join pipeline_dir, name + break + +unless config_dir then error "Failed to find matching target for config = #{config}" +app_path = Path\join config_dir, app, app .. os.osselect win:'.exe', unix:'' + +unless File\exists app_path then error "Failed to find executable: #{app_path}" + Dir\enter '../../build', -> - test = Path\join "bin/#{arch}/Windows-#{config}-msvc-#{arch}-v143/#{app}/#{app}.exe" - Log\info "Trying to run app: #{test}" - os.execute test + Log\info "Executing app: #{app_path}" + os.execute app_path diff --git a/tools/settings.json b/tools/settings.json index 06bd40e7f..d861ea9e3 100644 --- a/tools/settings.json +++ b/tools/settings.json @@ -40,5 +40,8 @@ }, "doxy": { "config": "source/configs/doxyfile" + }, + "vulkan": { + "version": "1.4.313.0" } }