diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index e2081ed50cc..08a84cbe101 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -202,7 +202,6 @@ jobs: lcov \ --capture \ --directory build/src \ - --directory build/modules \ --output-file coverage/coverage.info # remove external dependencies lcov \ diff --git a/.github/workflows/test-macos-gpu.yml b/.github/workflows/test-macos-gpu.yml index 86d5c7d3a0f..e2e5297c605 100644 --- a/.github/workflows/test-macos-gpu.yml +++ b/.github/workflows/test-macos-gpu.yml @@ -171,7 +171,6 @@ jobs: lcov \ --capture \ --directory build/src \ - --directory build/modules \ --output-file coverage/coverage.info \ --ignore-errors inconsistent,gcov,range,format # remove external dependencies diff --git a/cmake/Modules.cmake b/cmake/Modules.cmake index d15da019561..63ae45746b5 100644 --- a/cmake/Modules.cmake +++ b/cmake/Modules.cmake @@ -7,24 +7,23 @@ option(SHARDS_WITH_DEFAULT "Default SHARDS_WITH_ state of modules" OFF) # The same happens for the rust modules # # If you have any dependencies between the two, you should link both targets against each other in a ciruclar fashion -# on the final executable target, e.g.: -# target_link_libraries(shards-rust_union shards-cpp-union) -# target_link_libraries(shards-cpp-union shards-rust-union) +# on the final executable target, e.g.: +# target_link_libraries(shards-rust_union shards-cpp-union) +# target_link_libraries(shards-cpp-union shards-rust-union) # # If any of the cpp modules for example has a dependency that needs any rust code (for example, gfx, gfx-core) # you need to link in that dependency as an OBJECT library as well, so it will be in the same static union lib # # Since you can not chain OBJECT libraries in cmake (it will miss the lowest level objects) # you can set up an intermediate interface target like this: -# add_library(gfx-texture-file-obj OBJECT texture_file/texture_file.cpp) +# add_library(gfx-texture-file-obj OBJECT texture_file/texture_file.cpp) # -# add_library(gfx-texture-file INTERFACE) -# target_sources(gfx-texture-file PUBLIC $) -# target_link_libraries(gfx-texture-file INTERFACE gfx-texture-file-obj) +# add_library(gfx-texture-file INTERFACE) +# target_sources(gfx-texture-file PUBLIC $) +# target_link_libraries(gfx-texture-file INTERFACE gfx-texture-file-obj) # # gfx-texture-file can then be linked into a module as usual, e.g.: -# target_link_libraries(shards-module-gfx gfx-texture-file) - +# target_link_libraries(shards-module-gfx gfx-texture-file) function(is_module_enabled OUTPUT_VARIABLE MODULE_ID) if(${SHARDS_WITH_EVERYTHING}) set(${OUTPUT_VARIABLE} TRUE PARENT_SCOPE) @@ -207,6 +206,7 @@ function(shards_generate_rust_union TARGET_NAME) get_property(RUST_FEATURES TARGET ${RUST_TARGET} PROPERTY RUST_FEATURES) unset(RUST_FEATURES_STRING) unset(RUST_FEATURES_STRING1) + if(RUST_FEATURES) unset(RUST_FEATURES_QUOTED) @@ -257,6 +257,9 @@ function(shards_generate_rust_union TARGET_NAME) file(COPY_FILE ${CARGO_TOML}.tmp ${CARGO_TOML} ONLY_IF_DIFFERENT) unset(ENABLED_FEATURES) + if(TRACY_ENABLE) + list(APPEND ENABLED_FEATURES tracy) + endif() # Add the rust library add_rust_library(NAME ${TARGET_NAME} @@ -311,8 +314,8 @@ function(shards_generate_union UNION_TARGET_NAME) ) endif() - target_compile_definitions(${UNION_TARGET_NAME} PRIVATE - SHARDS_CORE_DLL=1 + target_compile_definitions(${UNION_TARGET_NAME} PRIVATE + SHARDS_CORE_DLL=1 shards_core_EXPORTS=1) foreach(TARGET_NAME ${SHARDS_MODULE_TARGETS}) @@ -321,6 +324,7 @@ function(shards_generate_union UNION_TARGET_NAME) if(UNION_EXTRA_ENABLED_MODULES) if(NOT "${MODULE_ID}" IN_LIST UNION_EXTRA_ENABLED_MODULES) message(DEBUG "Skipping module ${MODULE_ID}") + message(VERBOSE "Not enabled in set: ${UNION_EXTRA_ENABLED_MODULES}") continue() else() message(DEBUG "Including module ${MODULE_ID}") @@ -333,10 +337,16 @@ function(shards_generate_union UNION_TARGET_NAME) endif() endif() - message(DEBUG "${UNION_TARGET_NAME}: Adding module ${TARGET_NAME} (id: ${MODULE_ID})") - target_link_libraries(${UNION_TARGET_NAME} ${TARGET_NAME}) list(APPEND ENABLED_MODULE_IDS ${MODULE_ID}) list(APPEND ENABLED_MODULE_TARGETS ${TARGET_NAME}) + + set(DUP_TARGET_NAME "${UNION_TARGET_NAME}_${TARGET_NAME}") + duplicate_library_target(${TARGET_NAME} OBJECT ${DUP_TARGET_NAME}) + message(DEBUG "${UNION_TARGET_NAME}: Adding module ${TARGET_NAME} (as: ${DUP_TARGET_NAME}, id: ${MODULE_ID})") + target_link_libraries(${UNION_TARGET_NAME} ${DUP_TARGET_NAME}) + + # Add the generated include path to the module duplicate + target_include_directories(${DUP_TARGET_NAME} PUBLIC ${GENERATED_ROOT_DIR}) endforeach() # message(STATUS "ENABLED_MODULE_IDS ${ENABLED_MODULE_IDS}") @@ -349,7 +359,8 @@ function(shards_generate_union UNION_TARGET_NAME) set(GENERATED_TEMP "${CMAKE_CURRENT_BINARY_DIR}/temp.cpp") # Add include path to shards core so it can inline the code that is generated here - target_include_directories(shards-core PUBLIC ${GENERATED_ROOT_DIR}) + # target_include_directories(shards-core PUBLIC ${GENERATED_ROOT_DIR}) + target_include_directories(${UNION_TARGET_NAME} PUBLIC ${GENERATED_ROOT_DIR}) file(WRITE ${GENERATED_TEMP} "// This file is generated by CMake\n" diff --git a/cmake/Utils.cmake b/cmake/Utils.cmake index d78b6802b55..22f78d3dfe9 100644 --- a/cmake/Utils.cmake +++ b/cmake/Utils.cmake @@ -1,6 +1,6 @@ # Get all propreties that cmake supports if(NOT CMAKE_PROPERTIES_TO_DUPLICATE) - execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTIES_TO_DUPLICATE) + execute_process(COMMAND ${CMAKE_COMMAND} --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTIES_TO_DUPLICATE) # Convert command output into a CMake list string(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTIES_TO_DUPLICATE "${CMAKE_PROPERTIES_TO_DUPLICATE}") @@ -12,17 +12,21 @@ if(NOT CMAKE_PROPERTIES_TO_DUPLICATE) "INTERFACE_CXX_MODULE_HEADER_UNIT_SETS" "CXX_MODULE_HEADER_UNIT_SETS" "INTERFACE_CXX_MODULE_SETS" "CXX_MODULE_SETS" "BINARY_DIR" "IMPORTED" "SOURCE_DIR" + "MANUALLY_ADDED_DEPENDENCIES" ) foreach(IGNORED_PROPERTY ${IGNORED_PROPERTIES}) list(REMOVE_ITEM CMAKE_PROPERTIES_TO_DUPLICATE ${IGNORED_PROPERTY}) endforeach() + + message(VERBOSE "CMAKE_PROPERTIES_TO_DUPLICATE: ${CMAKE_PROPERTIES_TO_DUPLICATE}") endif() # Helper function that duplicates a library target into a new type # This allows defining a static library and duplicating it into a shared library target (with different defines, etc.) function(duplicate_library_target TARGET TYPE NEW_TARGET) add_library(${NEW_TARGET} ${TYPE}) + message(VERBOSE "Duplicating library target ${TARGET} into ${NEW_TARGET} of type ${TYPE}") foreach(PROPERTY ${CMAKE_PROPERTIES_TO_DUPLICATE}) string(REPLACE "" "${CMAKE_BUILD_TYPE}" PROPERTY ${PROPERTY}) @@ -37,6 +41,7 @@ function(duplicate_library_target TARGET TYPE NEW_TARGET) if(IS_PROPERTY_SET) get_target_property(VALUE ${TARGET} ${PROPERTY}) set_target_properties(${NEW_TARGET} PROPERTIES ${PROPERTY} "${VALUE}") + # message(VERBOSE "Setting ${PROPERTY} to ${VALUE}") endif() endforeach() diff --git a/cmake/rust/union_Cargo.toml.in b/cmake/rust/union_Cargo.toml.in index b0c057de2e2..1fc1377a130 100644 --- a/cmake/rust/union_Cargo.toml.in +++ b/cmake/rust/union_Cargo.toml.in @@ -6,10 +6,11 @@ version = "${CARGO_CRATE_VERSION}" edition = "2021" [features] -tracy = [] +tracy = ["tracy-client"] [dependencies] ${CARGO_CRATE_DEPS} +tracy-client = { version = "0.17.4", optional = true } [patch.crates-io] chrono = { git = "https://github.com/shards-lang/chrono", rev = "5aaf74235778120b5984b46ced47478c1431d9a0" } @@ -27,6 +28,8 @@ onig_sys = { git = "https://github.com/shards-lang/rust-onig.git", version = "=6 objc = { git = "https://github.com/shards-lang/rust-objc.git", version = "=0.2.7", branch = "shards-0.2.7" } metal = { git = "https://github.com/shards-lang/metal-rs.git", version = "=0.29.0", branch = "shards-0.29.0" } +tracy-client = { git = "https://github.com/nagisa/rust_tracy_client", tag = "tracy-client-v0.15.0" } + [profile.dev.package] tiny-skia = { opt-level = 3 } resvg = { opt-level = 3 } diff --git a/deps/entt b/deps/entt index 4a2d1a85412..42103b69a64 160000 --- a/deps/entt +++ b/deps/entt @@ -1 +1 @@ -Subproject commit 4a2d1a8541228a90e02a873dba6a980506c42c03 +Subproject commit 42103b69a6443c97c7db8dcfe7a6e97092eb4bd2 diff --git a/deps/spdlog b/deps/spdlog index be14e60d9e8..b18a234ed6a 160000 --- a/deps/spdlog +++ b/deps/spdlog @@ -1 +1 @@ -Subproject commit be14e60d9e8be31735dd9d2d132d8a4cd3482165 +Subproject commit b18a234ed6af38638678a3338c0d7ed90210ae6c diff --git a/include/shards/dllshard.hpp b/include/shards/dllshard.hpp index c7c9fbdae0c..31d3e111989 100644 --- a/include/shards/dllshard.hpp +++ b/include/shards/dllshard.hpp @@ -80,6 +80,10 @@ class Core { return sCore._core->referenceVariable(context, name); } + static SHVar *findVariable(SHContext *context, struct SHStringWithLen name) { + return sCore._core->findVariable(context, name); + } + static void releaseVariable(SHVar *variable) { return sCore._core->releaseVariable(variable); } static SHWireState suspend(SHContext *context, double seconds) { return sCore._core->suspend(context, seconds); } diff --git a/include/shards/shards.h b/include/shards/shards.h index e3eee724e0c..2645dc146f0 100644 --- a/include/shards/shards.h +++ b/include/shards/shards.h @@ -878,6 +878,7 @@ typedef void(__cdecl *SHUnregisterRunLoopCallback)(SHString eventName); typedef void(__cdecl *SHUnregisterExitCallback)(SHString eventName); typedef struct SHVar *(__cdecl *SHReferenceVariable)(struct SHContext *context, struct SHStringWithLen name); +typedef struct SHVar *(__cdecl *SHFindVariable)(struct SHContext *context, struct SHStringWithLen name); typedef struct SHVar *(__cdecl *SHReferenceWireVariable)(SHWireRef wire, struct SHStringWithLen name); typedef struct SHExternalVariable { @@ -1292,6 +1293,8 @@ typedef struct _SHCore { // We never needed it before but actually useful to expose for rust and swift shards SHReferenceVariable referenceGlobalVariable; + SHFindVariable findVariable; + //! ADD NEW FUNCTIONS AT BOTTOM OF THIS STRUCT } SHCore; diff --git a/include/shards/utility.hpp b/include/shards/utility.hpp index 74f23699c3a..7d677e31d0e 100644 --- a/include/shards/utility.hpp +++ b/include/shards/utility.hpp @@ -483,12 +483,12 @@ template struct TTableVar : public SHVar { TTableVar(const TTableVar &other) : SHVar() { SH_CORE::cloneVar(*this, other); } - TTableVar(const SHVar &other) : SHVar() { + explicit TTableVar(const SHVar &other) : SHVar() { assert(other.valueType == SHType::Table); SH_CORE::cloneVar(*this, other); } - TTableVar(SHVar &&other) : SHVar() { + explicit TTableVar(SHVar &&other) : SHVar() { assert(other.valueType == SHType::Table); std::swap(*this, *reinterpret_cast(&other)); } @@ -710,7 +710,7 @@ template struct TSeqVar : public SHVar { TSeqVar(TSeqVar &&other) : SHVar() { std::swap(*this, other); } - TSeqVar(SHVar &&other) : SHVar() { + explicit TSeqVar(SHVar &&other) : SHVar() { assert(other.valueType == SHType::Seq); std::swap(*this, other); } @@ -842,6 +842,49 @@ ALWAYS_INLINE inline void assignVariableValue(SHVar &v, const SHVar &other) { v.version = other.version; } +template struct TVarOrReference { + const SHVar *ptr; + SHVar *owned = nullptr; + + TVarOrReference(SHContext *context, const SHVar &v) : ptr(&v) { + if (v.valueType == SHType::ContextVar) { + if (auto var = SH_CORE::findVariable(context, toSWL(SHSTRVIEW(v)))) { + ptr = var; + owned = var; + } else { + throw std::runtime_error(fmt::format("Variable {} not found", SHSTRVIEW(v))); + } + } + } + + TVarOrReference(SHContext *context, const char *varName) : ptr(nullptr) { + if (auto var = SH_CORE::findVariable(context, toSWL(varName))) { + ptr = var; + owned = var; + } else { + throw std::runtime_error(fmt::format("Variable {} not found", varName)); + } + } + + ~TVarOrReference() { + if (owned) { + SH_CORE::releaseVariable(const_cast(owned)); + } + } + + TVarOrReference(const TVarOrReference &) = delete; + TVarOrReference &operator=(const TVarOrReference &) = delete; + TVarOrReference(TVarOrReference &&) = delete; + TVarOrReference &operator=(TVarOrReference &&) = delete; + + bool isVariable() const { return owned != nullptr; } + + const SHVar &get() const { return *ptr; } + operator const SHVar &() const { return *ptr; } + SHVar &get() { return const_cast(*ptr); } + operator SHVar &() { return const_cast(*ptr); } +}; + }; // namespace shards // specialize hash for TOwnedVar @@ -851,4 +894,6 @@ template struct hash> { }; } // namespace std +template auto format_as(const shards::TOwnedVar &ov) { return (SHVar &)ov; } + #endif diff --git a/lib/hot-reload.shs b/lib/hot-reload.shs index 923d9c5447a..cc3ef2490c5 100644 --- a/lib/hot-reload.shs +++ b/lib/hot-reload.shs @@ -1,4 +1,3 @@ -@include("std/if.shs" Once: true) @define(script-hot-reload false IgnoreRedefined: true) ; Builds wrapper wires around imported hot-reloadable wires @@ -148,8 +147,7 @@ }) @inline-template(maybe-hot-reload [script-path wires] { - @if-root(@script-hot-reload { - ; #(Log("Running hot reload macro")) + @if(@script-hot-reload { @hot-reload(@script-dir @include-dirs @namespace script-path wires) } { @include(script-path) diff --git a/lib/std/if.shs b/lib/std/if.shs index df24a41c75d..d33ac052e61 100644 --- a/lib/std/if.shs +++ b/lib/std/if.shs @@ -1,19 +1,35 @@ +@template(if-filter-ast [] { + If({ExpectTable | Take("shs") | IsSeq} { + ExpectTable | Take("shs") + } { + = in + [in] + } + ) +}) @macro(if-root [cond yes no] { - @template(filter-ast [] { - If({ExpectTable | Take("shs") | IsSeq} { - ExpectTable | Take("shs") - } { - = in - [in] - } - ) - }) - - Sequence(pipelines) cond | If(IsAny([true "true" 1]) { - @ast(yes) | FromJson | @filter-ast() + @ast(yes) | FromJson | @if-filter-ast() + } { + @ast(no) | FromJson | @if-filter-ast() + } + ) | ToJson +}) + +@macro(when [cond yes] { + cond | If(IsAny([true "true" 1]) { + @ast(yes) | FromJson | @if-filter-ast() + } { + [[]] + } + ) | ToJson +}) + +@macro(when-not [cond no] { + cond | If(IsAny([true "true" 1]) { + [[]] } { - @ast(no) | FromJson | @filter-ast() + @ast(no) | FromJson | @if-filter-ast() } ) | ToJson }) diff --git a/shards/core/check_utils.hpp b/shards/core/check_utils.hpp new file mode 100644 index 00000000000..0a4826eb3d8 --- /dev/null +++ b/shards/core/check_utils.hpp @@ -0,0 +1,25 @@ +#ifndef C25C0EEE_CC07_432E_9764_0EC84FA97064 +#define C25C0EEE_CC07_432E_9764_0EC84FA97064 + +#include +#include "exception.hpp" + +namespace shards { +inline void checkType(const SHType &type, SHType expectedType, const char *name) { + if (type != expectedType) + throw formatException("{} type should be {}, was {}", name, magic_enum::enum_name(expectedType), magic_enum::enum_name(type)); +} + +inline void checkEnumType(const SHVar &var, const shards::Type &expectedType, const char *name) { + checkType(var.valueType, SHType::Enum, name); + shards::Type actualType = shards::Type::Enum(var.payload.enumVendorId, var.payload.enumTypeId); + if (expectedType != actualType) { + SHTypeInfo typeInfoA = expectedType; + SHTypeInfo typeInfoB = actualType; + throw formatException("{} enum type should be {}/{}, was {}/{}", name, typeInfoA.enumeration.vendorId, + typeInfoA.enumeration.typeId, typeInfoB.enumeration.vendorId, typeInfoB.enumeration.typeId); + } +} +} // namespace shards + +#endif /* C25C0EEE_CC07_432E_9764_0EC84FA97064 */ diff --git a/shards/core/coro_annotations.inl b/shards/core/coro_annotations.inl new file mode 100644 index 00000000000..12a1ee74937 --- /dev/null +++ b/shards/core/coro_annotations.inl @@ -0,0 +1,87 @@ + +SHARDS_INLINE inline void coroResumed(SHContext *context) { + SHWire *wire = context->currentWire(); + if (!wire) + return; + +#if SH_DEBUG_THREAD_NAMES + shards::pushThreadName(wire->threadNameStrings.init(wire).resumeStr); +#endif + +#ifdef SH_VERBOSE_COROUTINES_LOGGING + SHLOG_TRACE("> Resumed wire {}", wire->name); +#endif + +#if SH_DEBUG + shassert(!context->isResumed); + context->isResumed = true; +#endif + + auto &logTs = shards::logging::ThreadState::get(); + if (context->linkedLogContext) { + // Push thread logging state + auto prevContext = logTs.current; + std::swap(context->prevLogContext, logTs.current); + // Reattach the parent log context, in case we are stepping from somewhere else + context->linkedLogContext->linkRootTo(prevContext); + } else { + // Push thread logging state + std::swap(context->prevLogContext, logTs.current); + } +} + +SHARDS_INLINE inline void coroSuspended(SHContext *context) { + SHWire *wire = context->currentWire(); + if (!wire) + return; + +#if SH_DEBUG + shassert(context->isResumed); + context->isResumed = false; +#endif + +#if SH_DEBUG_THREAD_NAMES + shards::popThreadName(); +#endif + +#ifdef SH_VERBOSE_COROUTINES_LOGGING + SHLOG_TRACE("< Suspended wire {}", wire->name); +#endif + + auto &logTs = shards::logging::ThreadState::get(); + if (context->linkedLogContext) { + shassert(context->prevLogContext != &*context->linkedLogContext && "Prev log context should not be linked log context"); + context->linkedLogContext->unlink(); + } + std::swap(context->prevLogContext, logTs.current); +} + +SHARDS_INLINE inline void coroExtResume(SHWire *wire) { + if (!wire) + return; + +#if SH_DEBUG_THREAD_NAMES + shards::pushThreadName(wire->threadNameStrings.init(wire).extResumeStr); +#endif + + TracyCoroEnter(wire); + +#ifdef SH_VERBOSE_COROUTINES_LOGGING + SHLOG_TRACE("Resuming wire {}", wire->name); +#endif +} + +SHARDS_INLINE inline void coroExtSuspend(SHWire *wire) { + if (!wire) + return; + +#if SH_DEBUG_THREAD_NAMES + shards::popThreadName(); +#endif + + TracyCoroExit(wire); + +#ifdef SH_VERBOSE_COROUTINES_LOGGING + SHLOG_TRACE("Suspending wire {}", wire->name); +#endif +} \ No newline at end of file diff --git a/shards/core/exception.hpp b/shards/core/exception.hpp new file mode 100644 index 00000000000..08f92880a11 --- /dev/null +++ b/shards/core/exception.hpp @@ -0,0 +1,15 @@ +#ifndef C87E09D4_EE39_461A_8FD7_741C3396CAB7 +#define C87E09D4_EE39_461A_8FD7_741C3396CAB7 + +#include +#include + +namespace shards { + +template std::runtime_error formatException(fmt::format_string format, TArgs &&...args) { + return std::runtime_error(fmt::format(format, std::forward(args)...)); +} + +} // namespace shards + +#endif /* C87E09D4_EE39_461A_8FD7_741C3396CAB7 */ diff --git a/shards/core/foundation.hpp b/shards/core/foundation.hpp index 0c6e19c7b33..54880aad0e4 100644 --- a/shards/core/foundation.hpp +++ b/shards/core/foundation.hpp @@ -1314,12 +1314,14 @@ struct InternalCore { } static SHVar *referenceVariable(SHContext *context, SHStringWithLen name) { - return shards::referenceVariable(context, std::string_view(name.string, name.len)); + return shards::referenceVariable(context, toStringView(name)); } static void stringGrow(SHStringPayload *str, uint32_t size) { shards::stringGrow(str, size); } static void stringFree(SHStringPayload *str) { shards::stringFree(str); } + static SHVar *findVariable(SHContext *context, SHStringWithLen name) { return shards::findVariable(context, toStringView(name)); } + static void releaseVariable(SHVar *variable) { shards::releaseVariable(variable); } static void cloneVar(SHVar &dst, const SHVar &src) { shards::cloneVar(dst, src); } @@ -1361,6 +1363,7 @@ struct InternalCore { }; typedef TParamVar ParamVar; +typedef TVarOrReference VarOrReference; template struct SimpleShard : public TSimpleShard {}; @@ -1753,4 +1756,20 @@ inline void swlFree(SHStringWithLen &in) { }; // namespace shards +inline auto format_as(SHWire::State state) { + return magic_enum::enum_name(state); +} + +inline const SHVar& format_as(const shards::SeqVar& v) { + return (SHVar&)v; +} + +inline const SHVar& format_as(const shards::TableVar& v) { + return (SHVar&)v; +} + +inline const SHVar& format_as(const shards::Var& v) { + return (SHVar&)v; +} + #endif // SH_CORE_FOUNDATION diff --git a/shards/core/ops_internal.hpp b/shards/core/ops_internal.hpp index 578ac4f7220..b7830eaf95c 100644 --- a/shards/core/ops_internal.hpp +++ b/shards/core/ops_internal.hpp @@ -8,6 +8,7 @@ #include #include "spdlog/fmt/bundled/core.h" #include +#include #include #include #include // must be included @@ -85,87 +86,75 @@ inline std::ostream &operator<<(std::ostream &os, const SHTypeInfo &v) { return inline std::ostream &operator<<(std::ostream &os, const SHTypesInfo &v) { return shards::defaultFormatter.format(os, v); } inline std::ostream &operator<<(std::ostream &os, const SHTrait &v) { return shards::defaultFormatter.format(os, v); } -template struct StringStreamFormatter { - constexpr auto parse(fmt::format_parse_context &ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it != end) - throw fmt::format_error("invalid format"); - return it; - } - - template auto format(const T &v, FormatContext &ctx) -> decltype(ctx.out()) { +template struct StringStreamFormatter { + template auto format(const T &v, FormatContext &ctx) const -> decltype(ctx.out()) { std::stringstream ss; ss << v; return fmt::format_to(ctx.out(), "{}", ss.str()); } }; -template <> struct fmt::formatter { +template <> struct fmt::formatter : fmt::formatter { StringStreamFormatter base; - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return base.parse(ctx); } - template auto format(const SHStringWithLen &v, FormatContext &ctx) -> decltype(ctx.out()) { + template auto format(const SHStringWithLen &v, FormatContext &ctx) const -> decltype(ctx.out()) { return base.format(v, ctx); } }; -template <> struct fmt::formatter { +template <> struct fmt::formatter : fmt::formatter { StringStreamFormatter base; - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return base.parse(ctx); } - template auto format(const SHTrait &v, FormatContext &ctx) -> decltype(ctx.out()) { + template auto format(const SHTrait &v, FormatContext &ctx) const -> decltype(ctx.out()) { return base.format(v, ctx); } }; -template <> struct fmt::formatter { +template <> struct fmt::formatter : fmt::formatter { StringStreamFormatter base; - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return base.parse(ctx); } - template auto format(const SHVar &v, FormatContext &ctx) -> decltype(ctx.out()) { + template auto format(const SHVar &v, FormatContext &ctx) const -> decltype(ctx.out()) { return base.format(v, ctx); } }; -template <> struct fmt::formatter { +template <> struct fmt::formatter : fmt::formatter { StringStreamFormatter base; - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return base.parse(ctx); } - template auto format(const SHTypeInfo &v, FormatContext &ctx) -> decltype(ctx.out()) { + template auto format(const SHTypeInfo &v, FormatContext &ctx) const -> decltype(ctx.out()) { return base.format(v, ctx); } }; -template <> struct fmt::formatter { +template <> struct fmt::formatter : fmt::formatter { StringStreamFormatter base; - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return base.parse(ctx); } - template auto format(const SHTypesInfo &v, FormatContext &ctx) -> decltype(ctx.out()) { + template auto format(const SHTypesInfo &v, FormatContext &ctx) const -> decltype(ctx.out()) { return base.format(v, ctx); } }; -template <> struct fmt::formatter { - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it != end) - throw format_error("invalid format"); - return it; - } - template auto format(const SHType &v, FormatContext &ctx) -> decltype(ctx.out()) { +template <> struct fmt::formatter : fmt::formatter { + template auto format(const SHType &v, FormatContext &ctx) const -> decltype(ctx.out()) { return format_to(ctx.out(), "{}", magic_enum::enum_name(v)); } }; -template <> struct fmt::formatter { +template <> struct fmt::formatter : fmt::formatter { fmt::formatter base; - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return base.parse(ctx); } - template auto format(const shards::Type &v, FormatContext &ctx) -> decltype(ctx.out()) { + template auto format(const shards::Type &v, FormatContext &ctx) const -> decltype(ctx.out()) { return base.format(v, ctx); } }; -template <> struct fmt::formatter { +template <> struct fmt::formatter : fmt::formatter { fmt::formatter base; - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return base.parse(ctx); } - template auto format(const shards::Types &v, FormatContext &ctx) -> decltype(ctx.out()) { + template auto format(const shards::Types &v, FormatContext &ctx) const -> decltype(ctx.out()) { return base.format(v, ctx); } }; +inline auto format_as(SHStringWithLen s) { + return shards::toStringView(s); +} + +inline auto format_as(SHWireState state) { + return magic_enum::enum_name(state); +} + #endif // SH_CORE_OPS_INTERNAL diff --git a/shards/core/pmr/shared_temp_allocator.cpp b/shards/core/pmr/shared_temp_allocator.cpp index ba0f6704615..60c1e1a6811 100644 --- a/shards/core/pmr/shared_temp_allocator.cpp +++ b/shards/core/pmr/shared_temp_allocator.cpp @@ -14,6 +14,14 @@ static shards::logging::Logger getLogger() { #define STA_TRACE(...) #endif +namespace std { +inline auto format_as(const std::thread::id& id) { + std::stringstream ss; + ss << id; + return (std::string)ss.str(); +} +} + namespace shards::pmr { struct SharedTempAllocatorImpl { TempAllocator allocator; @@ -28,9 +36,9 @@ struct SharedTempAllocatorImpl { void incRef() { if (refCount == 0) { + allocator.reset(); STA_TRACE("[{}] Temp allocator reset ({} bytes)", std::this_thread::get_id(), allocator.preallocatedBlock.size()); TracyPlot(debugName, (int64_t)allocator.preallocatedBlock.size()); - allocator.reset(); } ++refCount; } diff --git a/shards/core/runtime.cpp b/shards/core/runtime.cpp index 4774beb3c1b..7e545b0f148 100644 --- a/shards/core/runtime.cpp +++ b/shards/core/runtime.cpp @@ -891,93 +891,10 @@ void collectRequiredVariables(const SHInstanceData &data, ExposedInfo &out, cons } } -ALWAYS_INLINE void coroResumed(SHContext *context) { - SHWire *wire = context->currentWire(); - if (!wire) - return; - -#if SH_DEBUG_THREAD_NAMES - shards::pushThreadName(wire->threadNameStrings.init(wire).resumeStr); -#endif - -#ifdef SH_VERBOSE_COROUTINES_LOGGING - SHLOG_TRACE("> Resumed wire {}", wire->name); -#endif - -#if SH_DEBUG - shassert(!context->isResumed); - context->isResumed = true; -#endif - - auto &logTs = shards::logging::ThreadState::get(); - if (context->linkedLogContext) { - // Push thread logging state - auto prevContext = logTs.current; - std::swap(context->prevLogContext, logTs.current); - // Reattach the parent log context, in case we are stepping from somewhere else - context->linkedLogContext->linkRootTo(prevContext); - } else { - // Push thread logging state - std::swap(context->prevLogContext, logTs.current); - } -} - -ALWAYS_INLINE void coroSuspended(SHContext *context) { - SHWire *wire = context->currentWire(); - if (!wire) - return; - -#if SH_DEBUG - shassert(context->isResumed); - context->isResumed = false; -#endif - -#if SH_DEBUG_THREAD_NAMES - shards::popThreadName(); -#endif - -#ifdef SH_VERBOSE_COROUTINES_LOGGING - SHLOG_TRACE("< Suspended wire {}", wire->name); -#endif - - auto &logTs = shards::logging::ThreadState::get(); - if (context->linkedLogContext) { - shassert(context->prevLogContext != &*context->linkedLogContext && "Prev log context should not be linked log context"); - context->linkedLogContext->unlink(); - } - std::swap(context->prevLogContext, logTs.current); -} - -ALWAYS_INLINE void coroExtResume(SHWire *wire) { - if (!wire) - return; - -#if SH_DEBUG_THREAD_NAMES - shards::pushThreadName(wire->threadNameStrings.init(wire).extResumeStr); +#if !SHARDS_INLINE_EVERYTHING +#include "coro_annotations.inl" #endif - TracyCoroEnter(wire); - -#ifdef SH_VERBOSE_COROUTINES_LOGGING - SHLOG_TRACE("Resuming wire {}", wire->name); -#endif -} - -ALWAYS_INLINE void coroExtSuspend(SHWire *wire) { - if (!wire) - return; - -#if SH_DEBUG_THREAD_NAMES - shards::popThreadName(); -#endif - - TracyCoroExit(wire); - -#ifdef SH_VERBOSE_COROUTINES_LOGGING - SHLOG_TRACE("Suspending wire {}", wire->name); -#endif -} - void validateConnection(InternalCompositionContext &ctx) { ZoneScopedN("validateConnection"); ZoneName(ctx.bottom->name(ctx.bottom), ctx.bottom->nameLength); @@ -1619,7 +1536,7 @@ void error_handler(int err_sig) { if (crashed) { #ifndef __EMSCRIPTEN__ - SHLOG_ERROR(boost::stacktrace::stacktrace()); + SHLOG_ERROR("{}", boost::stacktrace::to_string(boost::stacktrace::stacktrace())); #endif auto handler = GetGlobals().CrashHandler; @@ -2768,6 +2685,11 @@ SHCore *__cdecl shardsInterface(uint32_t abi_version) { return shards::referenceWireVariable(wire, nameView); }; + result->findVariable = [](SHContext *context, SHStringWithLen name) noexcept { + std::string_view nameView{name.string, size_t(name.len)}; + return shards::findVariable(context, nameView); + }; + result->releaseVariable = [](SHVar *variable) noexcept { return shards::releaseVariable(variable); }; result->setExternalVariable = [](SHWireRef wire, SHStringWithLen name, SHExternalVariable *extVar) noexcept { diff --git a/shards/core/runtime.hpp b/shards/core/runtime.hpp index c89e619f23c..009711d88cf 100644 --- a/shards/core/runtime.hpp +++ b/shards/core/runtime.hpp @@ -24,6 +24,7 @@ #include "inline.hpp" #include "utils.hpp" #include "platform.hpp" +#include "ops_internal.hpp" #include #include @@ -318,6 +319,13 @@ void coroSuspended(SHContext *context); void coroExtResume(SHWire *wire); void coroExtSuspend(SHWire *wire); +#if SHARDS_INLINE_EVERYTHING +#define SHARDS_INLINE ALWAYS_INLINE +#include "coro_annotations.inl" +#else +#define SHARDS_INLINE +#endif + inline void prepare(SHWire *wire) { shassert(!coroutineValid(wire->coro) && "Wire already prepared!"); @@ -403,25 +411,28 @@ inline bool hasEnded(SHWire *wire) { return wire->state > SHWire::State::Iterati inline bool isCanceled(SHContext *context) { return context->shouldStop(); } -inline void sleep(double seconds = -1.0) { +inline void sleep(std::chrono::nanoseconds duration) { // negative = no sleep - if (seconds > 0.0) { + if (duration.count() > 0) { #ifdef _WIN32 HANDLE timer; LARGE_INTEGER ft; - ft.QuadPart = -(int64_t(seconds * 10000000)); + // Windows FILETIME uses 100-nanosecond intervals, so divide by 100 + ft.QuadPart = -(duration.count() / 100); timer = CreateWaitableTimer(NULL, TRUE, NULL); SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); WaitForSingleObject(timer, INFINITE); CloseHandle(timer); #elif __EMSCRIPTEN__ - unsigned int ms = floor(seconds * 1000.0); + // Convert nanoseconds to milliseconds + unsigned int ms = duration.count() / 1000000; emscripten_sleep(ms); #else struct timespec delay; - seconds += 0.5e-9; // add half epsilon - delay.tv_sec = (decltype(delay.tv_sec))seconds; - delay.tv_nsec = (seconds - delay.tv_sec) * 1000000000L; + // Convert nanoseconds to seconds and remaining nanoseconds + auto total_ns = duration.count(); + delay.tv_sec = total_ns / 1000000000L; + delay.tv_nsec = total_ns % 1000000000L; while (nanosleep(&delay, &delay)) (void)0; #endif @@ -431,6 +442,12 @@ inline void sleep(double seconds = -1.0) { } } +inline void sleep(double seconds = -1.0) { + // Convert seconds to nanoseconds and call the nanoseconds version + auto duration = std::chrono::duration_cast(std::chrono::duration(seconds)); + sleep(duration); +} + struct RuntimeCallbacks { // TODO, turn them into filters maybe? virtual void registerShard(const char *fullName, SHShardConstructor constructor) = 0; diff --git a/shards/core/trait.cpp b/shards/core/trait.cpp index ceab34fee31..c5e3b4017f0 100644 --- a/shards/core/trait.cpp +++ b/shards/core/trait.cpp @@ -42,10 +42,10 @@ SHTrait cloneTrait(const SHTrait &other) { return result; } -template inline auto formatLineInto(std::string &str, const S &format_str, Args &&...args) { +template inline auto formatLineInto(std::string &str, fmt::format_string format_str, TArgs &&...args) { if (!str.empty()) str.push_back('\n'); - fmt::format_to(std::back_inserter(str), format_str, std::forward(args)...); + fmt::format_to(std::back_inserter(str), format_str, std::forward(args)...); } bool TraitMatcher::operator()(SHExposedTypesInfo exposedVariables, const SHTrait &trait) { diff --git a/shards/core/trait.hpp b/shards/core/trait.hpp index 2bed211317d..7529417c462 100644 --- a/shards/core/trait.hpp +++ b/shards/core/trait.hpp @@ -21,7 +21,7 @@ struct TraitMatcher { struct Trait : public SHTrait { ~Trait() { reset(); } - Trait() { memset(this, 0, sizeof(SHTrait)); } + Trait() { memset((SHTrait*)this, 0, sizeof(SHTrait)); } Trait(const SHTrait &other) : SHTrait(cloneTrait(other)) {} Trait(const Trait &other) : SHTrait(cloneTrait(other)) {} Trait &operator=(const SHTrait &other) { @@ -102,4 +102,8 @@ struct TraitRegister { } // namespace shards +inline auto format_as(const shards::Trait &trait) { + return (SHTrait&)trait; +} + #endif /* B7638520_BA4D_4989_BDC2_7F3533FE84B5 */ diff --git a/shards/core/wires.cpp b/shards/core/wires.cpp index 98cf513ffa5..e9f4b18f4b9 100644 --- a/shards/core/wires.cpp +++ b/shards/core/wires.cpp @@ -49,6 +49,7 @@ void SHWire::destroy() { #if SH_CORO_NEED_STACK_MEM if (stackMem) { ::operator delete[](stackMem, std::align_val_t{16}); + stackMem = nullptr; } #endif } diff --git a/shards/fast_string/fmt.hpp b/shards/fast_string/fmt.hpp index 3544a618a8b..d22f279d342 100644 --- a/shards/fast_string/fmt.hpp +++ b/shards/fast_string/fmt.hpp @@ -4,18 +4,11 @@ #include "fast_string.hpp" #include -// template<> struct fmt::formatter { -// constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { -// auto it = ctx.begin(), end = ctx.end(); -// if (it != end) -// throw format_error("invalid format"); -// return it; -// } - -// template -// auto format(const shards::fast_string::FastString &str, FormatContext &ctx) -> decltype(ctx.out()) { -// return format_to(ctx.out(), "{}", str.str()); -// } -// }; +template <> struct fmt::formatter : fmt::formatter { + template + auto format(const shards::fast_string::FastString &str, FormatContext &ctx) const -> decltype(ctx.out()) { + return format_to(ctx.out(), "{}", str.str()); + } +}; #endif /* C15A8B87_4A27_4D56_AD9E_4DA2AACA21FE */ diff --git a/shards/gfx/CMakeLists.txt b/shards/gfx/CMakeLists.txt index 50f2ac538d9..c71943dba71 100644 --- a/shards/gfx/CMakeLists.txt +++ b/shards/gfx/CMakeLists.txt @@ -3,7 +3,6 @@ option(SH_DEBUG "Enable context debug mode by default" OFF) set(GFX_SOURCES culling.cpp context.cpp - loop.cpp view.cpp gfx_wgpu.cpp renderer.cpp @@ -43,7 +42,7 @@ set(GFX_SOURCES unique_id.cpp text/font.cpp text/stb_fonts.cpp - text/text_placer.cpp + text/text_placer.cpp text/mesh_buffer.cpp ) @@ -183,10 +182,7 @@ if(HAS_WGPU_NATIVE) add_dependencies(gfx-core-obj cargo-gfx-rust) # Get include paths from gfx-rust and append them to the core library - get_target_property(GFX_RUST_INCLUDES gfx-rust INTERFACE_INCLUDE_DIRECTORIES) - message(STATUS "gfx-rust includes: ${GFX_RUST_INCLUDES}") - target_include_directories(gfx-core-obj PUBLIC ${GFX_RUST_INCLUDES}) - target_compile_definitions(gfx-core-obj PUBLIC WEBGPU_NATIVE=1) + target_link_libraries(gfx-core-obj wgpu-headers) else() target_link_libraries(gfx INTERFACE gfx-core) endif() diff --git a/shards/gfx/context.cpp b/shards/gfx/context.cpp index 7c0810e4a9a..8943259e9e8 100644 --- a/shards/gfx/context.cpp +++ b/shards/gfx/context.cpp @@ -240,7 +240,7 @@ struct ContextMainOutput { } if (preferredFormat == WGPUTextureFormat_Undefined) { - throw formatException("Failed to reconfigure Surface with format {}", preferredFormat); + throw formatException("Failed to reconfigure Surface with format {}", magic_enum::enum_name(preferredFormat)); } if (preferredFormat != swapchainFormat) { @@ -253,7 +253,7 @@ struct ContextMainOutput { shassert(wgpuSurface); shassert(swapchainFormat != WGPUTextureFormat_Undefined); - SPDLOG_LOGGER_DEBUG(logger, "Configuring surface width: {}, height: {}, format: {}", newSize.x, newSize.y, swapchainFormat); + SPDLOG_LOGGER_DEBUG(logger, "Configuring surface width: {}, height: {}, format: {}", newSize.x, newSize.y, magic_enum::enum_name(swapchainFormat)); currentSize = newSize; // Force flush all texture references before resizing @@ -368,7 +368,7 @@ void Context::init(Window &window, const ContextCreationOptions &inOptions) { void Context::init(const ContextCreationOptions &inOptions) { options = inOptions; - if (inOptions.overrideNativeWindowHandle) { + if (inOptions.overrideNativeWindowHandle) { mainOutput = std::make_shared(inOptions.overrideNativeWindowHandle, onFlushTextureReferences); } @@ -730,10 +730,10 @@ void Context::requestAdapter() { } extras.backends = instanceBackends; - if (const char *debug = SDL_getenv("SHARDS_GFX_DEBUG")) { + if (SDL_getenv("SHARDS_GFX_DEBUG")) { extras.flags |= WGPUInstanceFlag_Debug; } - if (const char *debug = SDL_getenv("SHARDS_GFX_VALIDATION")) { + if (SDL_getenv("SHARDS_GFX_VALIDATION")) { extras.flags |= WGPUInstanceFlag_Validation; } desc.nextInChain = &extras.chain; @@ -754,7 +754,7 @@ void Context::requestAdapter() { SPDLOG_LOGGER_DEBUG(logger, "Enumerating {} adapters", adapters.size()); bool useAnyAdapter = {}; - if (const char *v = SDL_getenv("GFX_ANY_ADAPTER")) { + if (SDL_getenv("GFX_ANY_ADAPTER")) { useAnyAdapter = true; } for (size_t i = 0; i < adapters.size(); i++) { @@ -771,8 +771,8 @@ void Context::requestAdapter() { adapterType: {} backendType: {} }})", - props.vendorID, props.architecture, props.deviceID, props.description, props.adapterType, - props.backendType); + props.vendorID, props.architecture, props.deviceID, props.description, + magic_enum::enum_name(props.adapterType), magic_enum::enum_name(props.backendType)); if (!adapterToUse && (useAnyAdapter || props.adapterType == WGPUAdapterType_DiscreteGPU)) { adapterToUse = adapter; backendType = props.backendType; diff --git a/shards/gfx/drawable_processors/mesh_drawable_processor.hpp b/shards/gfx/drawable_processors/mesh_drawable_processor.hpp index 90c748eb690..c272f0e2c4e 100644 --- a/shards/gfx/drawable_processors/mesh_drawable_processor.hpp +++ b/shards/gfx/drawable_processors/mesh_drawable_processor.hpp @@ -391,7 +391,7 @@ struct MeshDrawableProcessor final : public IDrawableProcessor { ZoneScoped; auto &storage = context.storage; - auto &allocator = context.storage.workerMemory; + auto &allocator = storage.workerMemory; const CachedPipeline &cachedPipeline = context.cachedPipeline; // NOTE: Memory is thrown away at end of frame, deconstructed by renderer diff --git a/shards/gfx/enums.cpp b/shards/gfx/enums.cpp index af5b19581c5..538437b18dd 100644 --- a/shards/gfx/enums.cpp +++ b/shards/gfx/enums.cpp @@ -89,7 +89,7 @@ const TextureFormatDesc &getTextureFormatDescription(WGPUTextureFormat pixelForm auto &textureFormatMap = getTextureFormatMap(); auto it = textureFormatMap.find(pixelFormat); if (it == textureFormatMap.end()) { - throw formatException("Unsupported texture input format", magic_enum::enum_name(pixelFormat)); + throw formatException("Unsupported texture input format: {}", magic_enum::enum_name(pixelFormat)); } return it->second; } diff --git a/shards/gfx/error_utils.hpp b/shards/gfx/error_utils.hpp index 7db75d8ba6f..59ec8bc48b1 100644 --- a/shards/gfx/error_utils.hpp +++ b/shards/gfx/error_utils.hpp @@ -1,12 +1,13 @@ #ifndef GFX_ERROR_UTILS #define GFX_ERROR_UTILS +#include #include #include namespace gfx { -template std::runtime_error formatException(const char *format, TArgs... args) { - return std::runtime_error(fmt::format(format, args...)); +template std::runtime_error formatException(fmt::format_string format, TArgs &&...args) { + return shards::formatException(format, std::forward(args)...); } } // namespace gfx diff --git a/shards/gfx/fmt.hpp b/shards/gfx/fmt.hpp index 8c29610d69b..390345bc9f6 100644 --- a/shards/gfx/fmt.hpp +++ b/shards/gfx/fmt.hpp @@ -2,18 +2,14 @@ #define F69DB8FE_C060_498E_914A_E5245FB65749 #include "linalg.hpp" +#include "unique_id.hpp" +#include "gfx_wgpu.hpp" +#include #include #include -template struct fmt::formatter> { - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it != end) - throw format_error("invalid format"); - return it; - } - - template auto format(const linalg::vec &vec, FormatContext &ctx) -> decltype(ctx.out()) { +template struct fmt::formatter> : fmt::formatter { + template auto format(const linalg::vec &vec, FormatContext &ctx) const -> decltype(ctx.out()) { using namespace linalg::ostream_overloads; std::stringstream ss; ss << vec; @@ -21,4 +17,14 @@ template struct fmt::formatter> { } }; +template <> struct fmt::formatter : fmt::formatter { + template auto format(const gfx::UniqueId &id, FormatContext &ctx) const -> decltype(ctx.out()) { + return format_to(ctx.out(), "{}", id.value); + } +}; + +inline auto format_as(WGPUTextureFormat texture) { + return magic_enum::enum_name(texture); +} + #endif /* F69DB8FE_C060_498E_914A_E5245FB65749 */ diff --git a/shards/gfx/gizmos/shapes.hpp b/shards/gfx/gizmos/shapes.hpp index 1f6e156fbda..e84a0fb3540 100644 --- a/shards/gfx/gizmos/shapes.hpp +++ b/shards/gfx/gizmos/shapes.hpp @@ -7,7 +7,7 @@ #include "../mesh.hpp" #include "../shader/blocks.hpp" #include "../../core/pool.hpp" -#include +#include namespace gfx { diff --git a/shards/gfx/gltf/animation.hpp b/shards/gfx/gltf/animation.hpp index f63e4807e5b..241e15c8272 100644 --- a/shards/gfx/gltf/animation.hpp +++ b/shards/gfx/gltf/animation.hpp @@ -2,9 +2,10 @@ #define A234D1C4_5FF6_468F_826F_BC431A25D754 #include "linalg.h" +#include #include #include -#include +#include #include #include #include @@ -21,6 +22,14 @@ enum class BuiltinTarget { None, }; +inline auto format_as(const gfx::animation::BuiltinTarget& v) { + return magic_enum::enum_name(v); +} + +inline auto format_as(const gfx::animation::Interpolation& v) { + return magic_enum::enum_name(v); +} + using Value = std::variant; struct Track { @@ -102,4 +111,5 @@ inline float Animation::getDuration() const { } // namespace gfx + #endif /* A234D1C4_5FF6_468F_826F_BC431A25D754 */ \ No newline at end of file diff --git a/shards/gfx/loop.cpp b/shards/gfx/loop.cpp deleted file mode 100644 index 8bc991124db..00000000000 --- a/shards/gfx/loop.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "loop.hpp" -#include - -namespace gfx { -Loop::Loop() : startTime(Clock::now()) {} - -bool Loop::beginFrame(double targetDeltaTime, double &outDeltaTime) { - TimePoint now = Clock::now(); - outDeltaTime = std::chrono::duration(now - lastFrameTime).count(); - if (!haveFirstFrame) { - outDeltaTime = 0.0f; - lastFrameTime = now; - haveFirstFrame = true; - return true; - } else if (outDeltaTime >= targetDeltaTime) { - lastFrameTime = now; - return true; - } - return false; -} - -double Loop::getAbsoluteTime() const { - TimePoint now = Clock::now(); - return std::chrono::duration(now - startTime).count(); -} -} // namespace gfx diff --git a/shards/gfx/loop.hpp b/shards/gfx/loop.hpp index c337f9832e2..b8b3db277a3 100644 --- a/shards/gfx/loop.hpp +++ b/shards/gfx/loop.hpp @@ -18,6 +18,26 @@ struct Loop { bool beginFrame(double targetDeltaTime, double &deltaTime); double getAbsoluteTime() const; }; +inline Loop::Loop() : startTime(Clock::now()) {} +inline bool Loop::beginFrame(double targetDeltaTime, double &outDeltaTime) { + TimePoint now = Clock::now(); + outDeltaTime = std::chrono::duration(now - lastFrameTime).count(); + if (!haveFirstFrame) { + outDeltaTime = 0.0f; + lastFrameTime = now; + haveFirstFrame = true; + return true; + } else if (outDeltaTime >= targetDeltaTime) { + lastFrameTime = now; + return true; + } + return false; +} + +inline double Loop::getAbsoluteTime() const { + TimePoint now = Clock::now(); + return std::chrono::duration(now - startTime).count(); +} } // namespace gfx #endif // GFX_LOOP diff --git a/shards/gfx/render_graph_builder.hpp b/shards/gfx/render_graph_builder.hpp index b25129cc8f5..98be080ee99 100644 --- a/shards/gfx/render_graph_builder.hpp +++ b/shards/gfx/render_graph_builder.hpp @@ -14,16 +14,9 @@ #include #include -template <> struct fmt::formatter { - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it != end) - throw format_error("invalid format"); - return it; - } - +template <> struct fmt::formatter : fmt::formatter { template - auto format(const gfx::detail::graph_build_data::FrameSizing &size, FormatContext &ctx) -> decltype(ctx.out()) { + auto format(const gfx::detail::graph_build_data::FrameSizing &size, FormatContext &ctx) const -> decltype(ctx.out()) { using namespace linalg::ostream_overloads; std::stringstream ss; std::visit( @@ -757,7 +750,7 @@ struct RenderGraphBuilder { auto copyStep = steps::Copy::create(RenderStepInput::make(copy.src->name), RenderStepOutput::make(RenderStepOutput::Named(copy.dst->name, copy.dst->format))); std::get(*copyStep.get()).name = - fmt::format("Copy {} {}=>{}", copy.src->name, copy.src->format, copy.dst->format); + fmt::format("Copy {} {}=>{}", copy.src->name, magic_enum::enum_name(copy.src->format), magic_enum::enum_name(copy.dst->format)); NodeBuildData ©Node = generatedNodes.emplace_back(copyStep, size_t(~0)); copyNode.inputs.emplace_back(copy.src); copyNode.outputs.emplace_back(copy.dst); diff --git a/shards/gfx/render_target.cpp b/shards/gfx/render_target.cpp index be88474d00e..86e54621d9c 100644 --- a/shards/gfx/render_target.cpp +++ b/shards/gfx/render_target.cpp @@ -1,5 +1,6 @@ #include "render_target.hpp" #include "fwd.hpp" +#include #include namespace gfx { diff --git a/shards/gfx/rust/CMakeLists.txt b/shards/gfx/rust/CMakeLists.txt index df07d43c95a..2b63ad7233a 100644 --- a/shards/gfx/rust/CMakeLists.txt +++ b/shards/gfx/rust/CMakeLists.txt @@ -10,9 +10,11 @@ file(GLOB_RECURSE EXTRA_SOURCES if(TRACY_ENABLE) message(STATUS "Building gfx crate with tracy profiling enabled") list(APPEND FEATURES tracy) + if(TRACY_ON_DEMAND) list(APPEND FEATURES tracy-on-demand) endif() + list(APPEND RUST_ENV TRACY_CLIENT_LIB=TracyClient # The lib file is called libTracyClient.a but this needs to be and -l compatible arg TRACY_CLIENT_LIB_PATH=$ @@ -40,8 +42,17 @@ if(WIN32) target_link_libraries(gfx-rust INTERFACE Userenv ws2_32 Bcrypt d3d12 d3dcompiler) endif() +# Headers only to wgpu-native +add_library(wgpu-headers INTERFACE) + +# Add dependency for rust build script +add_dependencies(wgpu-headers cargo-gfx-rust) + if(HAS_WGPU_NATIVE) - target_compile_definitions(gfx-rust INTERFACE WEBGPU_NATIVE=1) - target_include_directories(gfx-rust INTERFACE ${WGPU_NATIVE_PATH}/ffi) - target_include_directories(gfx-rust INTERFACE ${WGPU_NATIVE_PATH}/ffi/webgpu-headers) + target_compile_definitions(wgpu-headers INTERFACE WEBGPU_NATIVE=1) + target_include_directories(wgpu-headers INTERFACE + ${WGPU_NATIVE_PATH}/ffi + ${WGPU_NATIVE_PATH}/ffi/webgpu-headers) + + target_link_libraries(gfx-rust INTERFACE wgpu-headers) endif() diff --git a/shards/gfx/shader/entry_point.hpp b/shards/gfx/shader/entry_point.hpp index 52eb9a9c392..d4159b74a5c 100644 --- a/shards/gfx/shader/entry_point.hpp +++ b/shards/gfx/shader/entry_point.hpp @@ -2,8 +2,8 @@ #define GFX_SHADER_ENTRY_POINT #include "../fwd.hpp" +#include "../enums.hpp" #include "block.hpp" -#include #include #include diff --git a/shards/gfx/shader/fmt.hpp b/shards/gfx/shader/fmt.hpp index fa50cab403d..dcbe02fc4a6 100644 --- a/shards/gfx/shader/fmt.hpp +++ b/shards/gfx/shader/fmt.hpp @@ -8,16 +8,9 @@ #include #include -template <> struct fmt::formatter { - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it != end) - throw format_error("invalid format"); - return it; - } - +template <> struct fmt::formatter : fmt::formatter { template - auto format(const gfx::shader::NumType &fieldType, FormatContext &ctx) -> decltype(ctx.out()) { + auto format(const gfx::shader::NumType &fieldType, FormatContext &ctx) const -> decltype(ctx.out()) { auto baseTypeName = magic_enum::enum_name(fieldType.baseType); if (fieldType.matrixDimension > 1) { return format_to(ctx.out(), "{{{}, {}x{}}}", baseTypeName, fieldType.numComponents, fieldType.matrixDimension); @@ -27,15 +20,8 @@ template <> struct fmt::formatter { } }; -template <> struct fmt::formatter { - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it != end) - throw format_error("invalid format"); - return it; - } - - template auto format(const gfx::shader::Type &fieldType, FormatContext &ctx) -> decltype(ctx.out()) { +template <> struct fmt::formatter : fmt::formatter { + template auto format(const gfx::shader::Type &fieldType, FormatContext &ctx) const -> decltype(ctx.out()) { return std::visit( [&](auto &arg) { using T = std::decay_t; @@ -49,15 +35,8 @@ template <> struct fmt::formatter { } }; -template <> struct fmt::formatter { - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it != end) - throw format_error("invalid format"); - return it; - } - - template auto format(const gfx::shader::LayoutPath &lp, FormatContext &ctx) -> decltype(ctx.out()) { +template <> struct fmt::formatter : fmt::formatter { + template auto format(const gfx::shader::LayoutPath &lp, FormatContext &ctx) const -> decltype(ctx.out()) { for (size_t i = 0; i < lp.path.size(); i++) { if (i > 0) { format_to(ctx.out(), "."); diff --git a/shards/gfx/shader/generator.hpp b/shards/gfx/shader/generator.hpp index d466340eb88..bdfd6f950bd 100644 --- a/shards/gfx/shader/generator.hpp +++ b/shards/gfx/shader/generator.hpp @@ -11,8 +11,8 @@ #include "struct_layout.hpp" #include "temp_variable.hpp" #include "fwd.hpp" -#include -#include +#include +#include #include #include #include @@ -33,8 +33,8 @@ struct IGeneratorDynamicHandler { virtual bool createDynamicOutput(FastString name, NumType requestedType) { return false; } }; -template static GeneratorError formatError(const char *format, TArgs... args) { - return GeneratorError(fmt::format(format, args...)); +template static GeneratorError formatError(fmt::format_string format, TArgs&&... args) { + return GeneratorError(fmt::format(format, std::forward(args)...)); } struct GeneratorDefinitions { diff --git a/shards/gfx/shader/struct_layout.hpp b/shards/gfx/shader/struct_layout.hpp index f4de2b5101a..c35b310fa2c 100644 --- a/shards/gfx/shader/struct_layout.hpp +++ b/shards/gfx/shader/struct_layout.hpp @@ -8,6 +8,7 @@ #include "types.hpp" #include #include +#include #include #include #include diff --git a/shards/gfx/text/font.hpp b/shards/gfx/text/font.hpp index 7ca7a715461..bb270de8aa1 100644 --- a/shards/gfx/text/font.hpp +++ b/shards/gfx/text/font.hpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/shards/gfx/text/mesh_buffer.hpp b/shards/gfx/text/mesh_buffer.hpp index e359ae22c52..aabe1ac6ae2 100644 --- a/shards/gfx/text/mesh_buffer.hpp +++ b/shards/gfx/text/mesh_buffer.hpp @@ -1,9 +1,9 @@ #ifndef AAADCC4F_0596_4543_90E8_8B30232C111D #define AAADCC4F_0596_4543_90E8_8B30232C111D -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/shards/gfx/text/text_placer.hpp b/shards/gfx/text/text_placer.hpp index c4327931737..ec840d6c8a7 100644 --- a/shards/gfx/text/text_placer.hpp +++ b/shards/gfx/text/text_placer.hpp @@ -1,7 +1,7 @@ #ifndef TEXT_PLACER_HPP #define TEXT_PLACER_HPP -#include +#include #include #include #include "font.hpp" diff --git a/shards/gfx/texture.hpp b/shards/gfx/texture.hpp index cfcbddbef66..314b9d9c53c 100644 --- a/shards/gfx/texture.hpp +++ b/shards/gfx/texture.hpp @@ -136,7 +136,7 @@ struct Texture final { #if SH_GFX_CONTEXT_DATA_LOG_LIFETIME ~Texture() { - SPDLOG_LOGGER_DEBUG(getContextDataLogger(), "Texture {} ({}) destroyed", label, id); + SPDLOG_LOGGER_DEBUG(getContextDataLogger(), "Texture {} ({}) destroyed", label, id.value); } #endif diff --git a/shards/gfx/texture_view_cache.hpp b/shards/gfx/texture_view_cache.hpp index ff823ab3379..5f44deff27c 100644 --- a/shards/gfx/texture_view_cache.hpp +++ b/shards/gfx/texture_view_cache.hpp @@ -7,6 +7,7 @@ #include "wgpu_handle.hpp" #include "renderer_cache.hpp" #include "texture.hpp" +#include "fmt.hpp" #include "gfx_wgpu.hpp" #include "log.hpp" #include diff --git a/shards/lang/src/eval.rs b/shards/lang/src/eval.rs index 7dae74f2b06..7f6846e5845 100644 --- a/shards/lang/src/eval.rs +++ b/shards/lang/src/eval.rs @@ -2048,7 +2048,9 @@ impl<'e> VariableResolver<'e> { ("namespace", true) => { let namespace = self.e.full_namespace.clone(); let namespace = namespace.as_str(); - Ok(ResolvedVar::new_const(SVar::Cloned(ClonedVar::from(Var::ephemeral_string(namespace))))) + Ok(ResolvedVar::new_const(SVar::Cloned(ClonedVar::from( + Var::ephemeral_string(namespace), + )))) } _ => { if let Some(defined_value) = find_defined(&func.name, self.e).map(|x| x.clone()) { @@ -3304,6 +3306,38 @@ fn process_template( } } +fn eval_cond_value( + v: &Value, + e: &mut EvalEnv, + line_info: LineInfo, + cancellation_token: Arc, +) -> Result<(), ShardsError> { + match v { + Value::Shards(shards) => { + for stmt in &shards.statements { + eval_statement(stmt, e, cancellation_token.clone())?; + } + } + Value::Shard(shard) => { + add_shard(shard, line_info, e)?; + } + Value::None(_) => {} + _ => { + return Err( + ( + format!( + "if built-in function requires a Shards parameter, got {:?}", + v + ), + line_info, + ) + .into(), + ) + } + } + return Ok(()); +} + fn eval_pipeline( pipeline: &Pipeline, e: &mut EvalEnv, @@ -3806,6 +3840,65 @@ fn eval_pipeline( ) } } + ("if", true) => { + if let Some(ref params) = func.params { + let param_helper = ParamHelper::new(params); + let v = param_helper.get_param_by_name_or_index("Value", 0).ok_or( + ( + "if built-in function requires a Value parameter", + get_block_line_info(e, block), + ) + .into(), + )?; + let then_ = param_helper.get_param_by_name_or_index("Then", 1); + let else_ = param_helper.get_param_by_name_or_index("Else", 2); + + let v = resolve_var(&v.value, get_block_line_info(e, block), None, e)?.into_var(); + let vr = match &v { + SVar::Cloned(var) => &var.0, + SVar::NotCloned(var) => var, + }; + + if !vr.is_bool() { + return Err( + ( + "if built-in function requires a boolean parameter", + get_block_line_info(e, block), + ) + .into(), + ); + } + + if unsafe { vr.payload.__bindgen_anon_1.boolValue } { + if let Some(then_) = then_ { + eval_cond_value( + &then_.value, + e, + get_block_line_info(e, block), + cancellation_token.clone(), + )?; + } + } else { + if let Some(else_) = else_ { + eval_cond_value( + &else_.value, + e, + get_block_line_info(e, block), + cancellation_token.clone(), + )?; + } + } + Ok(()) + } else { + Err( + ( + "if built-in function requires a parameter", + get_block_line_info(e, block), + ) + .into(), + ) + } + } ("color", true) => { process_color_built_in_function(func, get_block_line_info(e, block), e) } diff --git a/shards/lang/src/sample1.shs b/shards/lang/src/sample1.shs index 24a985b1884..87a6131f9c1 100644 --- a/shards/lang/src/sample1.shs +++ b/shards/lang/src/sample1.shs @@ -63,7 +63,7 @@ a | Math.Add(#(a | Math.Subtract(c))) ; run at eval time and write result Do(w1) Do(w2) -@macro(if [cond yes no] { +@macro(if-1 [cond yes no] { Sequence(pipelines) cond | If(IsAny([true "true" 1]) { { @@ -84,6 +84,6 @@ Do(w2) [pipelines] | ToJson }) -@if(true none none) +@if-1(true none none) ; comment diff --git a/shards/modules/anim/CMakeLists.txt b/shards/modules/anim/CMakeLists.txt index a3bdd7ddf86..488aa57d199 100644 --- a/shards/modules/anim/CMakeLists.txt +++ b/shards/modules/anim/CMakeLists.txt @@ -4,4 +4,8 @@ set(SOURCES add_shards_module(anim SOURCES ${SOURCES} REGISTER_SHARDS anim) -target_link_libraries(shards-module-anim gfx-core) +target_include_directories(shards-module-anim PRIVATE + ${SHARDS_DIR}/anim + ${SHARDS_DIR}/shards # For gfx + ${SHARDS_DIR}/gfx # For gfx +) diff --git a/shards/modules/anim/anim.cpp b/shards/modules/anim/anim.cpp index 1024cae411f..8337ffcdc3b 100644 --- a/shards/modules/anim/anim.cpp +++ b/shards/modules/anim/anim.cpp @@ -1,11 +1,11 @@ -#include "anim/path.hpp" -#include "anim/types.hpp" +#include +#include #include #include "linalg.h" #include #include #include -#include +#include #include #include #include @@ -24,7 +24,7 @@ static auto getKeyframeValue(const SHVar &keyframe) { return ((TableVar &)keyfra static auto getKeyframeInterpolation(const SHVar &keyframe) { Var &v = ((TableVar &)keyframe).get(Var("Interpolation")); if (v.valueType == SHType::Enum) { - gfx::checkEnumType(v, ShardsTypes::InterpolationEnumInfo::Type, "Interpolation"); + shards::checkEnumType(v, ShardsTypes::InterpolationEnumInfo::Type, "Interpolation"); return (Interpolation)v.payload.enumValue; } return Interpolation::Linear; diff --git a/shards/modules/channels/channels.cpp b/shards/modules/channels/channels.cpp index a1c05415a19..e1ca6ac1882 100644 --- a/shards/modules/channels/channels.cpp +++ b/shards/modules/channels/channels.cpp @@ -87,6 +87,9 @@ struct Produce : public Base { _channel = get(_name); auto &receiverType = _inType.valueType == SHType::Type ? *_inType.payload.typeValue : data.inputType; _mpChannel = &getAndInitChannel(_channel, receiverType, _name.c_str()); + + SPDLOG_TRACE("Produce {} => {}", _name, (void*)_channel.get()); + return data.inputType; } @@ -165,10 +168,10 @@ struct BufferedConsumer { bool empty() { return buffer.empty(); } - operator SHVar() { + SHVar get(bool asSeq) { auto len = buffer.size(); assert(len > 0); - if (len > 1) { + if (asSeq) { SHVar res{}; res.valueType = SHType::Seq; res.payload.seqValue.elements = &buffer[0]; @@ -245,6 +248,7 @@ struct Consume : public Consumers { } _channel = get(_name); + SPDLOG_TRACE("Consume {} => {}", _name, (void*)_channel.get()); _mpChannel = &getAndInitChannel(_channel, *outTypePtr, _name.c_str()); if (_bufferSize == 1) { @@ -274,7 +278,7 @@ struct Consume : public Consumers { // check also for channel completion if (_mpChannel->closed) { if (!_storage.empty()) { - return _storage; + return _storage.get(_bufferSize > 1); } else { context->stopFlow(Var::Empty); return Var::Empty; @@ -287,7 +291,7 @@ struct Consume : public Consumers { _storage.add(std::move(output)); } - return _storage; + return _storage.get(_bufferSize > 1); } void cleanup(SHContext *context) { @@ -351,7 +355,7 @@ struct Listen : public Consumers { // check also for channel completion if (_bChannel->closed) { if (!_storage.empty()) { - return _storage; + return _storage.get(_bufferSize > 1); } else { context->stopFlow(Var::Empty); return Var::Empty; @@ -364,7 +368,7 @@ struct Listen : public Consumers { _storage.add(std::move(output)); } - return _storage; + return _storage.get(_bufferSize > 1); } }; @@ -437,6 +441,7 @@ std::shared_ptr get(const std::string &name) { _l.unlock(); std::scoped_lock _l1(mutex); auto sp = std::make_shared(); + SPDLOG_TRACE("Created new channel: {}", name); channels[name] = sp; return sp; } else { @@ -445,6 +450,7 @@ std::shared_ptr get(const std::string &name) { _l.unlock(); std::scoped_lock _l1(mutex); sp = std::make_shared(); + SPDLOG_TRACE("Created new channel: {}", name); channels[name] = sp; } return sp; diff --git a/shards/modules/clipboard/CMakeLists.txt b/shards/modules/clipboard/CMakeLists.txt index 7b5db2e325f..9ce1da69c3e 100644 --- a/shards/modules/clipboard/CMakeLists.txt +++ b/shards/modules/clipboard/CMakeLists.txt @@ -5,4 +5,4 @@ set(SOURCES add_shards_module(clipboard SOURCES ${SOURCES} REGISTER_SHARDS clipboard) -target_link_libraries(shards-module-clipboard gfx-core shards-module-gfx) +target_link_libraries(shards-module-clipboard gfx-core) diff --git a/shards/modules/core/core.cpp b/shards/modules/core/core.cpp index a739bc73de6..ecdf84447a3 100644 --- a/shards/modules/core/core.cpp +++ b/shards/modules/core/core.cpp @@ -20,102 +20,111 @@ void *SDL_AndroidGetActivity(void); namespace shards { struct JointOp { - std::vector _multiSortColumns; - - SHVar _inputVar{}; - SHVar *_input = nullptr; - SHVar _columns{}; - - static SHTypesInfo inputTypes() { return CoreInfo::NoneType; } - static SHTypesInfo outputTypes() { return CoreInfo::AnySeqType; } - static inline Type anyVarSeq = Type::VariableOf(CoreInfo::AnySeqType); static inline Type anyVarSeqSeq = Type::SeqOf(anyVarSeq); - static inline Parameters joinOpParams{{"From", SHCCSTR("The name of the sequence variable to edit in place."), {anyVarSeq}}, - {"Join", - SHCCSTR("Other columns to join sort/filter using the input (they " - "must be of the same length)."), - {anyVarSeq, anyVarSeqSeq}}}; + PARAM_PARAMVAR(from, "From", "The name of the sequence variable to edit in place.", {anyVarSeq}) + PARAM_PARAMVAR(join, "Join", "Other columns to join sort/filter using the input (they must be of the same length).", + {CoreInfo::NoneType, anyVarSeq, anyVarSeqSeq}) - void setParam(int index, const SHVar &value) { - switch (index) { - case 0: - cloneVar(_inputVar, value); - break; - case 1: - cloneVar(_columns, value); - cleanup(nullptr); - break; - default: - break; - } - } + PARAM_IMPL(PARAM_IMPL_FOR(from), PARAM_IMPL_FOR(join)) - SHVar getParam(int index) { - switch (index) { - case 0: - return _inputVar; - case 1: - return _columns; - default: - break; - } - throw SHException("Parameter out of range."); - } + std::vector _multiSortColumns; - void cleanup(SHContext *context) { - for (auto ref : _multiSortColumns) { - releaseVariable(ref); - } - _multiSortColumns.clear(); + static SHTypesInfo inputTypes() { return CoreInfo::NoneType; } + static SHTypesInfo outputTypes() { return CoreInfo::AnySeqType; } - if (_input) { - releaseVariable(_input); - _input = nullptr; + PARAM_REQUIRED_VARIABLES(); + SHTypeInfo compose(SHInstanceData &data) { return outputTypes().elements[0]; } + + void warmup(SHContext *ctx) { + // Setup multi-sort columns based on join parameter + if (!join.isNone()) { + if (join.get().valueType == SHType::Seq) { + // Multiple columns case + for (const auto &col : join.get()) { + if (col.valueType == SHType::ContextVar) { + ParamVar columnVar; + columnVar = col; + columnVar.warmup(ctx); + _multiSortColumns.emplace_back(std::move(columnVar)); + } + } + } else if (join.get().valueType == SHType::ContextVar) { + // Single column case + ParamVar columnVar; + columnVar = join.get(); + columnVar.warmup(ctx); + _multiSortColumns.emplace_back(std::move(columnVar)); + } } } - void warmup(SHContext *context) { - if (!_input) { - _input = referenceVariable(context, SHSTRVIEW(_inputVar)); + void cleanup(SHContext *ctx) { + for (auto &col : _multiSortColumns) { + col.cleanup(ctx); } + _multiSortColumns.clear(); } void ensureJoinSetup(SHContext *context) { - if (_columns.valueType != SHType::None) { - auto len = _input->payload.seqValue.len; + if (!join.isNone()) { + auto &inputSeq = from.get(); + if (inputSeq.valueType != SHType::Seq) { + throw ActivationError("JointOp: Input must be a sequence."); + } + + auto len = inputSeq.payload.seqValue.len; + + // Setup multi-sort columns if not already done if (_multiSortColumns.size() == 0) { - if (_columns.valueType == SHType::Seq) { - for (const auto &col : _columns) { - auto target = referenceVariable(context, SHSTRVIEW(col)); - if (target && target->valueType == SHType::Seq) { - auto mseqLen = target->payload.seqValue.len; - if (len != mseqLen) { - throw ActivationError("JointOp: All the sequences to be processed must have " - "the same length as the input sequence."); + if (join.get().valueType == SHType::Seq) { + // Multiple columns case + for (const auto &col : join.get()) { + if (col.valueType == SHType::ContextVar) { + ParamVar columnVar; + columnVar = col; + columnVar.warmup(context); + + // Validate the column sequence length + if (columnVar.get().valueType == SHType::Seq) { + auto mseqLen = columnVar.get().payload.seqValue.len; + if (len != mseqLen) { + throw ActivationError("JointOp: All the sequences to be processed must have " + "the same length as the input sequence."); + } } - _multiSortColumns.push_back(target); + + _multiSortColumns.emplace_back(std::move(columnVar)); } } - } else if (_columns.valueType == SHType::ContextVar) { // normal single context var - auto target = referenceVariable(context, SHSTRVIEW(_columns)); - if (target && target->valueType == SHType::Seq) { - auto mseqLen = target->payload.seqValue.len; + } else if (join.get().valueType == SHType::ContextVar) { + // Single column case + ParamVar columnVar; + columnVar = join.get(); + columnVar.warmup(context); + + // Validate the column sequence length + if (columnVar.get().valueType == SHType::Seq) { + auto mseqLen = columnVar.get().payload.seqValue.len; if (len != mseqLen) { throw ActivationError("JointOp: All the sequences to be processed must have " "the same length as the input sequence."); } - _multiSortColumns.push_back(target); } + + _multiSortColumns.emplace_back(std::move(columnVar)); } } else { - for (const auto &seqVar : _multiSortColumns) { - const auto &seq = seqVar->payload.seqValue; - auto mseqLen = seq.len; - if (len != mseqLen) { - throw ActivationError("JointOp: All the sequences to be processed must have " - "the same length as the input sequence."); + // Validate existing columns + for (const auto &colVar : _multiSortColumns) { + const auto &colSeq = colVar.get(); + if (colSeq.valueType == SHType::Seq) { + auto mseqLen = colSeq.payload.seqValue.len; + if (len != mseqLen) { + throw ActivationError("JointOp: All the sequences to be processed must have " + "the same length as the input sequence."); + } } } } @@ -123,22 +132,16 @@ struct JointOp { } }; -struct ActionJointOp : public JointOp { - ShardsVar _blks{}; - void cleanup(SHContext *context) { - _blks.cleanup(context); - JointOp::cleanup(context); - } +struct Sort : public JointOp { + PARAM_VAR(_desc, "Desc", "If sorting should be in descending order, defaults ascending.", {CoreInfo::BoolType}) + PARAM(ShardsVar, _key, "Key", "The shards to use to transform the collection's items before they are compared. Can be None.", + {CoreInfo::ShardsOrNone}) - void warmup(SHContext *ctx) { - JointOp::warmup(ctx); - _blks.warmup(ctx); - } -}; + PARAM_IMPL_DERIVED(JointOp, PARAM_IMPL_FOR(_desc), PARAM_IMPL_FOR(_key)) -struct Sort : public ActionJointOp { std::vector _multiSortKeys; - bool _desc = false; + + Sort() { _desc = Var(false); } void setup() { shardsKeyFn._bu = this; } @@ -151,55 +154,27 @@ struct Sort : public ActionJointOp { static SHOptionalString outputHelp() { return SHCCSTR("Output is the sorted sequence."); } - static inline Parameters paramsInfo{ - joinOpParams, - {{"Desc", SHCCSTR("If sorting should be in descending order, defaults ascending."), {CoreInfo::BoolType}}, - {"Key", - SHCCSTR("The shards to use to transform the collection's items " - "before they are compared. Can be None."), - {CoreInfo::ShardsOrNone}}}}; - - static SHParametersInfo parameters() { return paramsInfo; } - - void setParam(int index, const SHVar &value) { - switch (index) { - case 0: - case 1: - return JointOp::setParam(index, value); - case 2: - _desc = value.payload.boolValue; - break; - case 3: - _blks = value; - break; - default: - break; - } + void warmup(SHContext *ctx) { + PARAM_WARMUP(ctx); + JointOp::warmup(ctx); } - SHVar getParam(int index) { - switch (index) { - case 0: - case 1: - return JointOp::getParam(index); - case 2: - return Var(_desc); - case 3: - return _blks; - default: - break; - } - throw SHException("Parameter out of range."); + void cleanup(SHContext *ctx) { + JointOp::cleanup(ctx); + PARAM_CLEANUP(ctx); } SHTypeInfo compose(SHInstanceData &data) { - if (_inputVar.valueType != SHType::ContextVar) + PARAM_COMPOSE_REQUIRED_VARIABLES(data); + + if (from.isNone()) { throw SHException("From variable was empty!"); + } SHExposedTypeInfo info{}; for (auto &reference : data.shared) { std::string_view name(reference.name); - if (name == SHSTRVIEW(_inputVar)) { + if (name == from.variableNameView()) { info = reference; goto found; } @@ -210,12 +185,12 @@ struct Sort : public ActionJointOp { found: // need to replace input type of inner wire with inner of seq if (info.exposedType.seqTypes.len != 1) - throw SHException(fmt::format("From variable \"{}\" is not a single type SHType::Seq ({}).", SHSTRVIEW(_inputVar), + throw SHException(fmt::format("From variable \"{}\" is not a single type SHType::Seq ({}).", from.variableNameView(), info.exposedType.seqTypes)); auto inputType = info.exposedType; data.inputType = info.exposedType.seqTypes.elements[0]; - _blks.compose(data); + _key.compose(data); return inputType; } @@ -237,7 +212,7 @@ struct Sort : public ActionJointOp { SHVar _o; const SHVar &operator()(const SHVar &a) { - _bu->_blks.activate(_ctx, a, _o); + _bu->_key.activate(_ctx, a, _o); return _o; } } shardsKeyFn; @@ -251,7 +226,7 @@ struct Sort : public ActionJointOp { // joined seqs _multiSortKeys.clear(); for (const auto &seqVar : _multiSortColumns) { - const auto &col = seqVar->payload.seqValue; + const auto &col = seqVar.get().payload.seqValue; if (col.len != n) { throw ActivationError("Sort: All the sequences to be processed must have " "the same length as the input sequence."); @@ -266,7 +241,7 @@ struct Sort : public ActionJointOp { seq[j + 1] = seq[j]; // joined seqs for (const auto &seqVar : _multiSortColumns) { - const auto &col = seqVar->payload.seqValue; + const auto &col = seqVar.get().payload.seqValue; col.elements[j + 1] = col.elements[j]; } // main + join @@ -277,7 +252,7 @@ struct Sort : public ActionJointOp { // joined seq auto z = 0; for (const auto &seqVar : _multiSortColumns) { - const auto &col = seqVar->payload.seqValue; + const auto &col = seqVar.get().payload.seqValue; col.elements[j + 1] = _multiSortKeys[z++]; } } @@ -285,28 +260,34 @@ struct Sort : public ActionJointOp { SHVar activate(SHContext *context, const SHVar &input) { JointOp::ensureJoinSetup(context); - // Sort in plac - int64_t len = int64_t(_input->payload.seqValue.len); - if (_blks) { + // Sort in place + auto &inputSeq = from.get(); + int64_t len = int64_t(inputSeq.payload.seqValue.len); + if (_key) { shardsKeyFn._ctx = context; - if (!_desc) { - insertSort(_input->payload.seqValue.elements, len, sortAsc, shardsKeyFn); + if (!*_desc) { + insertSort(inputSeq.payload.seqValue.elements, len, sortAsc, shardsKeyFn); } else { - insertSort(_input->payload.seqValue.elements, len, sortDesc, shardsKeyFn); + insertSort(inputSeq.payload.seqValue.elements, len, sortDesc, shardsKeyFn); } } else { - if (!_desc) { - insertSort(_input->payload.seqValue.elements, len, sortAsc, noopKeyFn); + if (!*_desc) { + insertSort(inputSeq.payload.seqValue.elements, len, sortAsc, noopKeyFn); } else { - insertSort(_input->payload.seqValue.elements, len, sortDesc, noopKeyFn); + insertSort(inputSeq.payload.seqValue.elements, len, sortDesc, noopKeyFn); } } - return *_input; + return inputSeq; } }; -struct Remove : public ActionJointOp { - bool _fast = false; +struct Remove : public JointOp { + PARAM(ShardsVar, _predicate, "Predicate", "The shards to use as predicate, if true the item will be popped from the sequence.", + {CoreInfo::Shards}) + PARAM_VAR(_fast, "Unordered", "Turn on to remove items very quickly but will not preserve the sequence items order.", + {CoreInfo::BoolType}) + + PARAM_IMPL_DERIVED(JointOp, PARAM_IMPL_FOR(_predicate), PARAM_IMPL_FOR(_fast)) static SHOptionalString help() { return SHCCSTR("Removes all elements from a sequence that match the given condition. Can also take these matched indices and " @@ -317,57 +298,29 @@ struct Remove : public ActionJointOp { static SHOptionalString outputHelp() { return SHCCSTR("Output is the filtered sequence."); } - static inline Parameters paramsInfo{joinOpParams, - {{"Predicate", - SHCCSTR("The shards to use as predicate, if true the " - "item will be popped from the sequence."), - {CoreInfo::Shards}}, - {"Unordered", - SHCCSTR("Turn on to remove items very quickly but will " - "not preserve the sequence items order."), - {CoreInfo::BoolType}}}}; + Remove() { _fast = Var(false); } - static SHParametersInfo parameters() { return paramsInfo; } - - void setParam(int index, const SHVar &value) { - switch (index) { - case 0: - case 1: - return JointOp::setParam(index, value); - case 2: - _blks = value; - break; - case 3: - _fast = value.payload.boolValue; - break; - default: - break; - } + void warmup(SHContext *ctx) { + PARAM_WARMUP(ctx); + JointOp::warmup(ctx); } - SHVar getParam(int index) { - switch (index) { - case 0: - case 1: - return JointOp::getParam(index); - case 2: - return _blks; - case 3: - return Var(_fast); - default: - break; - } - throw SHException("Parameter out of range."); + void cleanup(SHContext *ctx) { + PARAM_CLEANUP(ctx); + JointOp::cleanup(ctx); } SHTypeInfo compose(SHInstanceData &data) { - if (_inputVar.valueType != SHType::ContextVar) + PARAM_COMPOSE_REQUIRED_VARIABLES(data); + + if (from.isNone()) { throw SHException("From variable was empty!"); + } SHExposedTypeInfo info{}; for (auto &reference : data.shared) { std::string_view name(reference.name); - if (name == SHSTRVIEW(_inputVar)) { + if (name == from.variableNameView()) { info = reference; goto found; } @@ -378,12 +331,12 @@ struct Remove : public ActionJointOp { found: // need to replace input type of inner wire with inner of seq if (info.exposedType.seqTypes.len != 1) - throw SHException(fmt::format("From variable \"{}\" is not a single type SHType::Seq ({}).", SHSTRVIEW(_inputVar), + throw SHException(fmt::format("From variable \"{}\" is not a single type SHType::Seq ({}).", from.variableNameView(), info.exposedType.seqTypes)); auto inputType = info.exposedType; data.inputType = info.exposedType.seqTypes.elements[0]; - const auto pres = _blks.compose(data); + const auto pres = _predicate.compose(data); if (pres.outputType.basicType != SHType::Bool) { throw ComposeError("Remove Predicate should output a boolean value."); } @@ -394,27 +347,28 @@ struct Remove : public ActionJointOp { JointOp::ensureJoinSetup(context); // Remove in place, will possibly remove any sorting! SHVar output{}; - const uint32_t len = _input->payload.seqValue.len; + auto &inputSeq = from.get(); + const uint32_t len = inputSeq.payload.seqValue.len; for (uint32_t i = len; i > 0; i--) { - const auto &var = _input->payload.seqValue.elements[i - 1]; + const auto &var = inputSeq.payload.seqValue.elements[i - 1]; // conditional flow so we might have "returns" form (And) (Or) - if (unlikely(_blks.activate(context, var, output) > SHWireState::Return)) - return *_input; + if (unlikely(_predicate.activate(context, var, output) > SHWireState::Return)) + return inputSeq; if (output == Var::True) { // this is acceptable cos del ops don't call free or grow - if (_fast) - arrayDelFast(_input->payload.seqValue, i - 1); + if (*_fast) + arrayDelFast(inputSeq.payload.seqValue, i - 1); else - arrayDel(_input->payload.seqValue, i - 1); + arrayDel(inputSeq.payload.seqValue, i - 1); // remove from joined - for (const auto &seqVar : _multiSortColumns) { - auto &seq = seqVar->payload.seqValue; - if (seq.elements == _input->payload.seqValue.elements) // avoid removing from same seq as input! + for (auto &seqVar : _multiSortColumns) { + auto &seq = seqVar.get().payload.seqValue; + if (seq.elements == inputSeq.payload.seqValue.elements) // avoid removing from same seq as input! continue; if (seq.len >= i) { - if (_fast) + if (*_fast) arrayDelFast(seq, i - 1); else arrayDel(seq, i - 1); @@ -422,18 +376,18 @@ struct Remove : public ActionJointOp { } } } - return *_input; + return inputSeq; } }; struct Profile { - ShardsVar _shards{}; + PARAM(ShardsVar, _action, "Action", "The action shards to profile.", {CoreInfo::Shards}) + PARAM_VAR(_label, "Label", "The label to print when outputting time data.", {CoreInfo::StringType}) + + PARAM_IMPL(PARAM_IMPL_FOR(_action), PARAM_IMPL_FOR(_label)) + SHExposedTypesInfo _exposed{}; SHExposedTypesInfo _required{}; - std::string _label{""}; - - static inline Parameters _params{{"Action", SHCCSTR("The action shards to profile."), {CoreInfo::Shards}}, - {"Label", SHCCSTR("The label to print when outputting time data."), {CoreInfo::StringType}}}; static SHOptionalString help() { return SHCCSTR("This shard outputs the amount of time it took to execute the shards provided in the Action parameter, " @@ -450,14 +404,15 @@ struct Profile { static SHTypesInfo inputTypes() { return CoreInfo::AnyType; } static SHTypesInfo outputTypes() { return CoreInfo::AnyType; } - static SHParametersInfo parameters() { return _params; } + PARAM_REQUIRED_VARIABLES(); - void cleanup(SHContext *context) { _shards.cleanup(context); } + void cleanup(SHContext *context) { PARAM_CLEANUP(context); } - void warmup(SHContext *ctx) { _shards.warmup(ctx); } + void warmup(SHContext *ctx) { PARAM_WARMUP(ctx); } - SHTypeInfo compose(const SHInstanceData &data) { - auto res = _shards.compose(data); + SHTypeInfo compose(SHInstanceData &data) { + PARAM_COMPOSE_REQUIRED_VARIABLES(data); + auto res = _action.compose(data); _exposed = res.exposedInfo; _required = res.requiredInfo; return res.outputType; @@ -465,33 +420,6 @@ struct Profile { SHExposedTypesInfo exposedVariables() { return _exposed; } - SHExposedTypesInfo requiredVariables() { return _required; } - - void setParam(int index, const SHVar &value) { - switch (index) { - case 0: - _shards = value; - break; - case 1: - _label = SHSTRVIEW(value); - break; - default: - break; - } - } - - SHVar getParam(int index) { - switch (index) { - case 0: - return _shards; - case 1: - return Var(_label); - default: - break; - } - throw SHException("Parameter out of range."); - } - std::string formatDuration(int64_t nanoseconds) { if (nanoseconds < 1000) { return fmt::format("{} ns", nanoseconds); @@ -507,7 +435,7 @@ struct Profile { SHVar activate(SHContext *context, const SHVar &input) { SHVar output{}; const auto start = std::chrono::high_resolution_clock::now(); - _shards.activate(context, input, output); + _action.activate(context, input, output); const auto stop = std::chrono::high_resolution_clock::now(); const auto dur = std::chrono::duration_cast(stop - start).count(); SHLOG_INFO("{} took {}", _label, formatDuration(dur)); @@ -2618,23 +2546,6 @@ RUNTIME_SHARD_warmup(RTake); RUNTIME_SHARD_activate(RTake); RUNTIME_SHARD_END(RTake); -// Register Slice -RUNTIME_CORE_SHARD_FACTORY(Slice); -RUNTIME_SHARD_destroy(Slice); -RUNTIME_SHARD_cleanup(Slice); -RUNTIME_SHARD_requiredVariables(Slice); -RUNTIME_SHARD_help(Slice); -RUNTIME_SHARD_inputTypes(Slice); -RUNTIME_SHARD_inputHelp(Slice); -RUNTIME_SHARD_outputTypes(Slice); -RUNTIME_SHARD_outputHelp(Slice); -RUNTIME_SHARD_parameters(Slice); -RUNTIME_SHARD_compose(Slice); -RUNTIME_SHARD_setParam(Slice); -RUNTIME_SHARD_getParam(Slice); -RUNTIME_SHARD_activate(Slice); -RUNTIME_SHARD_END(Slice); - // Register Limit RUNTIME_CORE_SHARD_FACTORY(Limit); RUNTIME_SHARD_destroy(Limit); @@ -2665,39 +2576,6 @@ RUNTIME_SHARD_getParam(RLimit); RUNTIME_SHARD_activate(RLimit); RUNTIME_SHARD_END(RLimit); -// Register Sort -RUNTIME_CORE_SHARD(Sort); -RUNTIME_SHARD_setup(Sort); -RUNTIME_SHARD_help(Sort); -RUNTIME_SHARD_inputTypes(Sort); -RUNTIME_SHARD_inputHelp(Sort); -RUNTIME_SHARD_outputTypes(Sort); -RUNTIME_SHARD_outputHelp(Sort); -RUNTIME_SHARD_compose(Sort); -RUNTIME_SHARD_activate(Sort); -RUNTIME_SHARD_parameters(Sort); -RUNTIME_SHARD_setParam(Sort); -RUNTIME_SHARD_getParam(Sort); -RUNTIME_SHARD_cleanup(Sort); -RUNTIME_SHARD_warmup(Sort); -RUNTIME_SHARD_END(Sort); - -// Register Remove -RUNTIME_CORE_SHARD(Remove); -RUNTIME_SHARD_help(Remove); -RUNTIME_SHARD_inputTypes(Remove); -RUNTIME_SHARD_inputHelp(Remove); -RUNTIME_SHARD_outputTypes(Remove); -RUNTIME_SHARD_outputHelp(Remove); -RUNTIME_SHARD_parameters(Remove); -RUNTIME_SHARD_setParam(Remove); -RUNTIME_SHARD_getParam(Remove); -RUNTIME_SHARD_activate(Remove); -RUNTIME_SHARD_cleanup(Remove); -RUNTIME_SHARD_warmup(Remove); -RUNTIME_SHARD_compose(Remove); -RUNTIME_SHARD_END(Remove); - LOGIC_OP_DESC(IsAny); LOGIC_OP_DESC(IsAll); LOGIC_OP_DESC(IsAnyNot); @@ -3260,13 +3138,13 @@ SHARDS_REGISTER_FN(core) { REGISTER_CORE_SHARD(IsValidNumber); REGISTER_CORE_SHARD(Take); REGISTER_CORE_SHARD(RTake); - REGISTER_CORE_SHARD(Slice); REGISTER_SHARD("Split", Split); + REGISTER_SHARD("Slice", Slice); REGISTER_CORE_SHARD(Limit); REGISTER_CORE_SHARD(RLimit); REGISTER_SHARD("Repeat", Repeat); - REGISTER_CORE_SHARD(Sort); - REGISTER_CORE_SHARD(Remove); + REGISTER_SHARD("Sort", Sort); + REGISTER_SHARD("Remove", Remove); REGISTER_SHARD("Is", Is); REGISTER_SHARD("IsAlmost", IsAlmost); diff --git a/shards/modules/core/core.hpp b/shards/modules/core/core.hpp index 523d2da2872..0378e95f389 100644 --- a/shards/modules/core/core.hpp +++ b/shards/modules/core/core.hpp @@ -3574,49 +3574,48 @@ struct RTake : public Take { }; struct Slice { - static inline ParamsInfo indicesParamsInfo = ParamsInfo( - ParamsInfo::Param("From", - SHCCSTR("The position/index of the first character or element that is to be extracted (including). " - "Negative position/indices simply loop over the target string/sequence counting backwards."), - CoreInfo::IntsVar), - ParamsInfo::Param("To", - SHCCSTR("The position/index of the last character or element that is to be extracted (excluding). " - "Negative position/indices simply loop over the target string/sequence counting backwards."), - CoreInfo::IntsVarOrNone), - ParamsInfo::Param("Step", - SHCCSTR("The increment between each position/index. Chooses every nth sample to extract, where n is the " - "increment. Value has to be greater than zero."), - CoreInfo::IntType)); + struct OutOfRangeEx : public ActivationError { + OutOfRangeEx(int64_t len, int64_t from, int64_t to) + : ActivationError(fmt::format("Out of range! len: {} from: {} to: {}", len, from, to)) {} + }; + static SHOptionalString help() { return SHCCSTR("Extracts characters from a string or elements from a sequence based on the start and end positions/indices " "and an increment parameter. Operation is non-destructive; the target string/sequence is not modified."); } + PARAM_PARAMVAR(_from, "From", + "The position/index of the first character or element that is to be extracted (including). " + "Negative position/indices simply loop over the target string/sequence counting backwards.", + {CoreInfo::IntsVar}) + + PARAM_PARAMVAR(_to, "To", + "The position/index of the last character or element that is to be extracted (excluding). " + "Negative position/indices simply loop over the target string/sequence counting backwards.", + {CoreInfo::IntsVarOrNone}) + + PARAM_VAR(_step, "Step", + "The increment between each position/index. Chooses every nth sample to extract, where n is the " + "increment. Value has to be greater than zero.", + {CoreInfo::IntType}) + + PARAM_IMPL(PARAM_IMPL_FOR(_from), PARAM_IMPL_FOR(_to), PARAM_IMPL_FOR(_step)) + + PARAM_REQUIRED_VARIABLES(); + SHSeq _cachedSeq{}; std::vector _cachedBytes{}; - SHVar _from{shards::Var(0)}; - SHVar *_fromVar = nullptr; - SHVar _to{}; - SHVar *_toVar = nullptr; - ExposedInfo _exposedInfo{}; - int64_t _step = 1; - void destroy() { - destroyVar(_from); - destroyVar(_to); + Slice() { + _step = Var(1); + _from = Var(0); } void cleanup(SHContext *context) { - if (_fromVar) { - releaseVariable(_fromVar); - _fromVar = nullptr; - } - if (_toVar) { - releaseVariable(_toVar); - _toVar = nullptr; - } + PARAM_CLEANUP(context); + if (_cachedSeq.elements) { - if (_step > 1) { + if (_step->payload.intValue > 1) { // we cloned in this case for (auto i = _cachedSeq.len; i > 0; i--) { destroyVar(_cachedSeq.elements[i - 1]); @@ -3636,38 +3635,8 @@ struct Slice { static SHTypesInfo outputTypes() { return CoreInfo::AnyType; } static SHOptionalString outputHelp() { return SHCCSTR("The extracted characters/elements."); } - static SHParametersInfo parameters() { return SHParametersInfo(indicesParamsInfo); } - SHTypeInfo compose(const SHInstanceData &data) { - bool valid = false; - - if (_from.valueType == SHType::Int) { - valid = true; - } else { // SHType::ContextVar - for (auto &info : data.shared) { - if (info.name == SHSTRVIEW(_from)) { - valid = true; - break; - } - } - } - - if (!valid) - throw SHException("Slice, invalid From variable."); - - if (_to.valueType == SHType::Int || _to.valueType == SHType::None) { - valid = true; - } else { // SHType::ContextVar - for (auto &info : data.shared) { - if (info.name == SHSTRVIEW(_to)) { - valid = true; - break; - } - } - } - - if (!valid) - throw SHException("Slice, invalid To variable."); + PARAM_COMPOSE_REQUIRED_VARIABLES(data); if (data.inputType.basicType == SHType::Seq) { OVERRIDE_ACTIVATE(data, activateSeq); @@ -3680,76 +3649,15 @@ struct Slice { return data.inputType; } - SHExposedTypesInfo requiredVariables() { - // stringValue should be null terminated cos from and to are cloned! - if (_from.valueType == SHType::ContextVar && _to.valueType == SHType::ContextVar) { - _exposedInfo = - ExposedInfo(ExposedInfo::Variable(_from.payload.stringValue, SHCCSTR("The required variable."), CoreInfo::IntType), - ExposedInfo::Variable(_to.payload.stringValue, SHCCSTR("The required variable."), CoreInfo::IntType)); - return SHExposedTypesInfo(_exposedInfo); - } else if (_from.valueType == SHType::ContextVar) { - _exposedInfo = - ExposedInfo(ExposedInfo::Variable(_from.payload.stringValue, SHCCSTR("The required variable."), CoreInfo::IntType)); - return SHExposedTypesInfo(_exposedInfo); - } else if (_to.valueType == SHType::ContextVar) { - _exposedInfo = - ExposedInfo(ExposedInfo::Variable(_to.payload.stringValue, SHCCSTR("The required variable."), CoreInfo::IntType)); - return SHExposedTypesInfo(_exposedInfo); - } else { - return {}; - } - } - - void setParam(int index, const SHVar &value) { - switch (index) { - case 0: - cloneVar(_from, value); - cleanup(nullptr); - break; - case 1: - cloneVar(_to, value); - cleanup(nullptr); - break; - case 2: - _step = value.payload.intValue; - default: - break; - } - } - - SHVar getParam(int index) { - switch (index) { - case 0: - return _from; - case 1: - return _to; - case 2: - return shards::Var(_step); - default: - break; - } - return shards::Var::Empty; - } - - struct OutOfRangeEx : public ActivationError { - OutOfRangeEx(int64_t len, int64_t from, int64_t to) : ActivationError("Slice out of range!") { - SHLOG_ERROR("Out of range! from: {} to: {} len: {}", from, to, len); - } - }; + void warmup(SHContext *ctx) { PARAM_WARMUP(ctx); } SHVar activateBytes(SHContext *context, const SHVar &input) { - if (_from.valueType == SHType::ContextVar && !_fromVar) { - _fromVar = referenceVariable(context, SHSTRVIEW(_from)); - } - if (_to.valueType == SHType::ContextVar && !_toVar) { - _toVar = referenceVariable(context, SHSTRVIEW(_to)); - } - const auto inputLen = input.payload.bytesSize; - const auto &vfrom = _fromVar ? *_fromVar : _from; - const auto &vto = _toVar ? *_toVar : _to; + const auto &vfrom = _from.get(); + const auto &vto = _to.get(); SHInt from = vfrom.payload.intValue; SHInt to = vto.valueType == SHType::None ? SHInt(inputLen) : vto.payload.intValue; + SHInt step = _step->payload.intValue; // Convert negative indices to positive from = from < 0 ? SHInt(inputLen) + from : from; @@ -3766,15 +3674,15 @@ struct Slice { } uint32_t len = uint32_t(to - from); - if (_step <= 0) { + if (step <= 0) { throw ActivationError("Slice's Step must be greater than 0"); } - uint32_t actualLen = len / _step + (len % _step != 0 ? 1 : 0); - _cachedBytes.resize(actualLen); // Allocate sufficient space + uint32_t actualLen = len / step + (len % step != 0 ? 1 : 0); + _cachedBytes.resize(actualLen); uint32_t idx = 0; - for (int i = from; i < to && idx < actualLen; i += _step) { + for (int i = from; i < to && idx < actualLen; i += step) { if (uint32_t(i) >= inputLen) { throw OutOfRangeEx(inputLen, i, i); } @@ -3784,23 +3692,17 @@ struct Slice { } SHVar activateString(SHContext *context, const SHVar &input) { - if (_from.valueType == SHType::ContextVar && !_fromVar) { - _fromVar = referenceVariable(context, SHSTRVIEW(_from)); - } - if (_to.valueType == SHType::ContextVar && !_toVar) { - _toVar = referenceVariable(context, SHSTRVIEW(_to)); - } - if (input.payload.stringValue == nullptr) { throw ActivationError("Input string is null."); } uint32_t inputLen = input.payload.stringLen > 0 ? input.payload.stringLen : uint32_t(strlen(input.payload.stringValue)); - const auto &vfrom = _fromVar ? *_fromVar : _from; - const auto &vto = _toVar ? *_toVar : _to; + const auto &vfrom = _from.get(); + const auto &vto = _to.get(); SHInt from = vfrom.payload.intValue; SHInt to = vto.valueType == SHType::None ? int(inputLen) : vto.payload.intValue; + SHInt step = _step->payload.intValue; // Convert negative indices to positive from = from < 0 ? SHInt(inputLen) + from : from; @@ -3817,37 +3719,31 @@ struct Slice { } uint32_t len = uint32_t(to - from); - if (_step <= 0) { + if (step <= 0) { throw ActivationError("Slice's Step must be greater than 0"); } - uint32_t actualLen = len / _step + (len % _step != 0 ? 1 : 0); - _cachedBytes.resize(actualLen + 1); // +1 for the null terminator + uint32_t actualLen = len / step + (len % step != 0 ? 1 : 0); + _cachedBytes.resize(actualLen + 1); uint32_t idx = 0; - for (int i = from; i < to && idx < actualLen; i += _step) { + for (int i = from; i < to && idx < actualLen; i += step) { if (uint32_t(i) >= inputLen) { throw OutOfRangeEx(inputLen, i, i); } _cachedBytes[idx++] = input.payload.stringValue[i]; } - _cachedBytes[idx] = '\0'; // Ensure null termination + _cachedBytes[idx] = '\0'; return shards::Var((const char *)_cachedBytes.data(), actualLen); } SHVar activateSeq(SHContext *context, const SHVar &input) { - if (_from.valueType == SHType::ContextVar && !_fromVar) { - _fromVar = referenceVariable(context, SHSTRVIEW(_from)); - } - if (_to.valueType == SHType::ContextVar && !_toVar) { - _toVar = referenceVariable(context, SHSTRVIEW(_to)); - } - const auto inputLen = input.payload.seqValue.len; - const auto &vfrom = _fromVar ? *_fromVar : _from; - const auto &vto = _toVar ? *_toVar : _to; + const auto &vfrom = _from.get(); + const auto &vto = _to.get(); SHInt from = vfrom.payload.intValue; SHInt to = vto.valueType == SHType::None ? SHInt(inputLen) : vto.payload.intValue; + SHInt step = _step->payload.intValue; // Convert negative indices to positive from = from < 0 ? SHInt(inputLen) + from : from; @@ -3864,11 +3760,11 @@ struct Slice { } const auto len = to - from; - if (_step <= 0) { + if (step <= 0) { throw ActivationError("Slice's Step must be greater than 0"); } - if (_step == 1) { + if (step == 1) { // Optimization for step of 1 SHVar output{}; output.valueType = SHType::Seq; @@ -3877,10 +3773,10 @@ struct Slice { return output; } else { // General case for step greater than 1 - const auto actualLen = len / _step + (len % _step != 0 ? 1 : 0); + const auto actualLen = len / step + (len % step != 0 ? 1 : 0); shards::arrayResize(_cachedSeq, uint32_t(actualLen)); auto idx = 0; - for (int i = from; i < to && idx < actualLen; i += _step) { + for (int i = from; i < to && idx < actualLen; i += step) { if (uint32_t(i) >= inputLen) { throw OutOfRangeEx(inputLen, i, i); } @@ -4601,7 +4497,6 @@ RUNTIME_CORE_SHARD_TYPE(Get); RUNTIME_CORE_SHARD_TYPE(Swap); RUNTIME_CORE_SHARD_TYPE(Take); RUNTIME_CORE_SHARD_TYPE(RTake); -RUNTIME_CORE_SHARD_TYPE(Slice); RUNTIME_CORE_SHARD_TYPE(Limit); RUNTIME_CORE_SHARD_TYPE(RLimit); RUNTIME_CORE_SHARD_TYPE(Push); diff --git a/shards/modules/fs/fs.cpp b/shards/modules/fs/fs.cpp index 543814003ea..9c8d761749b 100644 --- a/shards/modules/fs/fs.cpp +++ b/shards/modules/fs/fs.cpp @@ -20,6 +20,12 @@ namespace fs = boost::filesystem; using ErrorCode = boost::system::error_code; +namespace boost::filesystem { +auto format_as(const path& p) { + return p.string(); +} +} + namespace shards { namespace FS { @@ -138,6 +144,31 @@ struct Extension { } }; +struct ReplaceExtension { + std::string _output; + + static SHTypesInfo inputTypes() { return CoreInfo::StringType; } + static SHTypesInfo outputTypes() { return CoreInfo::StringType; } + + PARAM_PARAMVAR(_newExtension, "NewExtension", "The new extension to replace the existing extension with", {CoreInfo::StringOrStringVar}); + PARAM_IMPL(PARAM_IMPL_FOR(_newExtension)); + + PARAM_REQUIRED_VARIABLES() + SHTypeInfo compose(const SHInstanceData &data) { + PARAM_COMPOSE_REQUIRED_VARIABLES(data); + return data.inputType; + } + void warmup(SHContext *context) { PARAM_WARMUP(context); } + void cleanup(SHContext *context) { PARAM_CLEANUP(context); } + + SHVar activate(SHContext *context, const SHVar &input) { + _output.clear(); + fs::path p(SHSTRING_PREFER_SHSTRVIEW(input)); + p = p.replace_extension(SHSTRING_PREFER_SHSTRVIEW(_newExtension.get())); + _output.assign(p.string()); + return Var(_output); + } +}; struct IsFile { static SHTypesInfo inputTypes() { return CoreInfo::StringType; } static SHTypesInfo outputTypes() { return CoreInfo::BoolType; } @@ -757,33 +788,34 @@ struct IsNotAny { } }; -}; // namespace FS - SHARDS_REGISTER_FN(fs) { - REGISTER_ENUM(FS::Copy::IfExistsEnumInfo); - - REGISTER_SHARD("FS.Join", FS::Join); - REGISTER_SHARD("FS.Iterate", FS::Iterate); - REGISTER_SHARD("FS.Extension", FS::Extension); - REGISTER_SHARD("FS.Filename", FS::Filename); - REGISTER_SHARD("FS.RelativeTo", FS::RelativeTo); - REGISTER_SHARD("FS.Parent", FS::Parent); - REGISTER_SHARD("FS.Read", FS::Read); - REGISTER_SHARD("FS.Write", FS::Write); - REGISTER_SHARD("FS.IsFile", FS::IsFile); - REGISTER_SHARD("FS.IsDirectory", FS::IsDirectory); - REGISTER_SHARD("FS.Copy", FS::Copy); - REGISTER_SHARD("FS.Remove", FS::Remove); - REGISTER_SHARD("FS.RemoveAll", FS::RemoveAll); - REGISTER_SHARD("FS.LastWriteTime", FS::LastWriteTime); - REGISTER_SHARD("FS.SetWriteTime", FS::SetWriteTime); - REGISTER_SHARD("FS.CreateDirectories", FS::CreateDirectories); - REGISTER_SHARD("FS.Absolute", FS::Absolute); - REGISTER_SHARD("FS.IsAbsolute", FS::IsAbsolute); - REGISTER_SHARD("FS.Normalize", FS::Normalize); - REGISTER_SHARD("FS.Rename", FS::Rename); - REGISTER_SHARD("FS.Is", FS::Is); - REGISTER_SHARD("FS.IsAny", FS::IsAny); - REGISTER_SHARD("FS.IsNotAny", FS::IsNotAny); + REGISTER_ENUM(Copy::IfExistsEnumInfo); + + REGISTER_SHARD("FS.Join", Join); + REGISTER_SHARD("FS.Iterate", Iterate); + REGISTER_SHARD("FS.Extension", Extension); + REGISTER_SHARD("FS.ReplaceExtension", ReplaceExtension); + REGISTER_SHARD("FS.Filename", Filename); + REGISTER_SHARD("FS.RelativeTo", RelativeTo); + REGISTER_SHARD("FS.Parent", Parent); + REGISTER_SHARD("FS.Read", Read); + REGISTER_SHARD("FS.Write", Write); + REGISTER_SHARD("FS.IsFile", IsFile); + REGISTER_SHARD("FS.IsDirectory", IsDirectory); + REGISTER_SHARD("FS.Copy", Copy); + REGISTER_SHARD("FS.Remove", Remove); + REGISTER_SHARD("FS.RemoveAll", RemoveAll); + REGISTER_SHARD("FS.LastWriteTime", LastWriteTime); + REGISTER_SHARD("FS.SetWriteTime", SetWriteTime); + REGISTER_SHARD("FS.CreateDirectories", CreateDirectories); + REGISTER_SHARD("FS.Absolute", Absolute); + REGISTER_SHARD("FS.IsAbsolute", IsAbsolute); + REGISTER_SHARD("FS.Normalize", Normalize); + REGISTER_SHARD("FS.Rename", Rename); + REGISTER_SHARD("FS.Is", Is); + REGISTER_SHARD("FS.IsAny", IsAny); + REGISTER_SHARD("FS.IsNotAny", IsNotAny); } + +}; // namespace FS }; // namespace shards diff --git a/shards/modules/gfx/buffer_vars.hpp b/shards/modules/gfx/buffer_vars.hpp index d634dc0ab14..874e849745a 100644 --- a/shards/modules/gfx/buffer_vars.hpp +++ b/shards/modules/gfx/buffer_vars.hpp @@ -35,7 +35,7 @@ inline void validateVertexFormat(const std::vector &format, if ((seq.len % format.size()) != 0) { throw formatException("Invalid number of vertices ({}), needs to be a " - "multiple of {} (based on layout)", + "multiple of {} (based on layout)", seq.len, format.size()); } diff --git a/shards/modules/gfx/camera.hpp b/shards/modules/gfx/camera.hpp index 3a0b5995417..6b588ae11a2 100644 --- a/shards/modules/gfx/camera.hpp +++ b/shards/modules/gfx/camera.hpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include namespace gfx { diff --git a/shards/modules/gfx/drawable_utils.hpp b/shards/modules/gfx/drawable_utils.hpp index 97bb25e7f90..85086ab5251 100644 --- a/shards/modules/gfx/drawable_utils.hpp +++ b/shards/modules/gfx/drawable_utils.hpp @@ -171,7 +171,7 @@ inline void initShaderParams(SHContext *shContext, const SHTable ¶msTable, M } auto kv = SHSTRVIEW(key); - ReferencedVar ref(shContext, v); + VarOrReference ref(shContext, v); auto param = tryVarToParam(ref); if (param) { std::visit([&](auto &&arg) { out.set(kv, arg); }, std::move(param.value())); @@ -188,7 +188,7 @@ inline bool initShaderParamsIfChanged(SHContext *shContext, const SHTable ¶m } auto kv = SHSTRVIEW(key); - ReferencedVar ref(shContext, v); + VarOrReference ref(shContext, v); auto param = tryVarToParam(ref); if (param) { std::visit( diff --git a/shards/modules/gfx/feature.cpp b/shards/modules/gfx/feature.cpp index c4ddfe1e85c..04599cdc1f5 100644 --- a/shards/modules/gfx/feature.cpp +++ b/shards/modules/gfx/feature.cpp @@ -591,19 +591,9 @@ struct FeatureShard { } } - static ShaderParamVariant paramVarToShaderParameter(SHContext *context, SHVar v) { - SHVar *ref{}; - if (v.valueType == SHType::ContextVar) { - ref = referenceVariable(context, SHSTRVIEW(v)); - v = *ref; - } - - auto shaderParam = varToShaderParameter(v); - - if (ref) - releaseVariable(ref); - - return shaderParam; + static ShaderParamVariant paramVarToShaderParameter(SHContext *context, SHVar v_) { + VarOrReference v(context, v_); + return varToShaderParameter(v); } // Returns the field type, or std::monostate if not specified diff --git a/shards/modules/gfx/gfx.hpp b/shards/modules/gfx/gfx.hpp index ec1921a814f..b2487a08a19 100644 --- a/shards/modules/gfx/gfx.hpp +++ b/shards/modules/gfx/gfx.hpp @@ -3,22 +3,22 @@ #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 diff --git a/shards/modules/gfx/gizmos/context.hpp b/shards/modules/gfx/gizmos/context.hpp index f0b71cc8ad4..e09a0219647 100644 --- a/shards/modules/gfx/gizmos/context.hpp +++ b/shards/modules/gfx/gizmos/context.hpp @@ -5,9 +5,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include namespace shards { // Shards for rendering visual helpers diff --git a/shards/modules/gfx/shader/composition.hpp b/shards/modules/gfx/shader/composition.hpp index 8091bb03a0e..4e8211adf6e 100644 --- a/shards/modules/gfx/shader/composition.hpp +++ b/shards/modules/gfx/shader/composition.hpp @@ -10,6 +10,7 @@ #include "../shards_utils.hpp" namespace gfx::shader { +using shards::VarOrReference; using VariableMap = std::unordered_map; using VariableRemapping = std::map; struct ShaderCompositionContext { @@ -71,7 +72,7 @@ void applyComposeWithHashed(SHContext *context, const SHVar &input, SHVar &hash, XXH3_128bits_reset_withSecret(&hashState, CUSTOM_XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); for (auto &[k, v] : input.payload.tableValue) { if (v.valueType == SHType::ContextVar) { - gfx::ReferencedVar pv(context, v); + VarOrReference pv(context, v); shHashState.updateHash(pv.get(), &hashState); } else { uint8_t constData = 0xff; @@ -88,7 +89,7 @@ void applyComposeWithHashed(SHContext *context, const SHVar &input, SHVar &hash, if (k.valueType != SHType::String) throw formatException("ComposeWith key must be a string"); std::string keyStr(SHSTRVIEW(k)); - gfx::ReferencedVar pv(context, v); + VarOrReference pv(context, v); auto &var = composedWith.emplace(std::move(keyStr), pv.get()).first->second; if (var.valueType == SHType::None) { throw formatException("Required variable {} not found", k); diff --git a/shards/modules/gfx/shader/math_shards.cpp b/shards/modules/gfx/shader/math_shards.cpp index 1beb1f56e1d..b0613b7f13d 100644 --- a/shards/modules/gfx/shader/math_shards.cpp +++ b/shards/modules/gfx/shader/math_shards.cpp @@ -50,9 +50,9 @@ template struct ToNumberTranslator { std::string prefix = fmt::format("{}((", getWGSLTypeName(unitFieldType)); std::string suffix{}; if (requireSourceSwizzle) - suffix = fmt::format((isLast ? ").{})" : ").{}), "), componentNames[i]); + suffix = isLast ? fmt::format(").{})", componentNames[i]) : fmt::format(").{}), ", componentNames[i]); else - suffix = fmt::format((isLast ? "))" : ")), ")); + suffix = isLast ? "))" : ")), "; sourceComponentList->children.emplace_back( blocks::makeCompoundBlock(std::move(prefix), std::move(inner), std::move(suffix))); @@ -80,7 +80,7 @@ template struct ToNumberTranslator { if (!isLast) fmt += ", "; - compound->children.emplace_back(blocks::makeBlock(fmt::format(fmt, 0.))); + compound->children.emplace_back(blocks::makeBlock(fmt::format(fmt::runtime(fmt), 0.))); } compound->children.emplace_back(blocks::makeBlock(")")); diff --git a/shards/modules/gfx/shards_utils.hpp b/shards/modules/gfx/shards_utils.hpp index 242c43223f9..b0d750f09bb 100644 --- a/shards/modules/gfx/shards_utils.hpp +++ b/shards/modules/gfx/shards_utils.hpp @@ -2,6 +2,7 @@ #define AD2CA4AE_4D00_49A0_8DD6_323B82813690 #include +#include #include #include #include @@ -13,72 +14,23 @@ #include "shards_types.hpp" namespace gfx { +using shards::checkType; +using shards::checkEnumType; -struct ReferencedVar { - const SHVar *ptr; - SHVar *owned = nullptr; - - ReferencedVar(SHContext *context, const SHVar &v) : ptr(&v) { - if (v.valueType == SHType::ContextVar) { - if (auto var = shards::findVariable(context, SHSTRVIEW(v))) { - ptr = var; - owned = var; - } - } - } - - ~ReferencedVar() { - if (owned) { - shards::releaseVariable(const_cast(owned)); - } - } - - ReferencedVar(const ReferencedVar &) = delete; - ReferencedVar &operator=(const ReferencedVar &) = delete; - ReferencedVar(ReferencedVar &&) = delete; - ReferencedVar &operator=(ReferencedVar &&) = delete; - - bool isVariable() const { return owned != nullptr; } - - const SHVar &get() const { return *ptr; } - operator const SHVar &() const { return *ptr; } - SHVar &get() { return const_cast(*ptr); } - operator SHVar &() { return const_cast(*ptr); } -}; +using shards::VarOrReference; // Retrieves a value directly or from a context variable from a table by name // returns false if the table does not contain an entry for that key inline bool getFromTable(SHContext *shContext, const SHTable &table, const SHVar &key, SHVar &outVar) { if (table.api->tableContains(table, key)) { const SHVar *var = table.api->tableAt(table, key); - if (var->valueType == SHType::ContextVar) { - SHVar *refencedVariable = shards::referenceVariable(shContext, SHSTRVIEW((*var))); - outVar = *refencedVariable; - shards::releaseVariable(refencedVariable); - } else { - outVar = *var; - } + VarOrReference varOrReference(shContext, *var); + outVar = varOrReference.get(); return true; } return false; } -inline void checkType(const SHType &type, SHType expectedType, const char *name) { - if (type != expectedType) - throw formatException("{} type should be {}, was {}", name, magic_enum::enum_name(expectedType), magic_enum::enum_name(type)); -} - -inline void checkEnumType(const SHVar &var, const shards::Type &expectedType, const char *name) { - checkType(var.valueType, SHType::Enum, name); - shards::Type actualType = shards::Type::Enum(var.payload.enumVendorId, var.payload.enumTypeId); - if (expectedType != actualType) { - SHTypeInfo typeInfoA = expectedType; - SHTypeInfo typeInfoB = actualType; - throw formatException("{} enum type should be {}/{}, was {}/{}", name, typeInfoA.enumeration.vendorId, - typeInfoA.enumeration.typeId, typeInfoB.enumeration.vendorId, typeInfoB.enumeration.typeId); - } -} - inline void applyFeatures(SHContext *context, std::vector &outFeatures, const SHVar &input) { checkType(input.valueType, SHType::Seq, ":Features"); for (size_t i = 0; i < input.payload.seqValue.len; i++) { diff --git a/shards/modules/gfx/steps.cpp b/shards/modules/gfx/steps.cpp index 9b4542c207d..07ec1ad5b69 100644 --- a/shards/modules/gfx/steps.cpp +++ b/shards/modules/gfx/steps.cpp @@ -171,7 +171,7 @@ std::optional toOutputScale(const SHVar &input) { } void applyOutputScale(SHContext *context, RenderStepOutput::OutputSizing &sizing, const SHVar &input) { - ReferencedVar v(context, input); + VarOrReference v(context, input); if (input.valueType == SHType::Table) { SHTable &table = v.get().payload.tableValue; diff --git a/shards/modules/gfx/view.cpp b/shards/modules/gfx/view.cpp index 9e7e1d8ca5c..af8ec15cb15 100644 --- a/shards/modules/gfx/view.cpp +++ b/shards/modules/gfx/view.cpp @@ -283,7 +283,7 @@ struct RenderIntoShard { TextureSubResource applyAttachment(SHContext *shContext, const SHVar &input) { if (input.valueType == SHType::ContextVar) { - ReferencedVar var(shContext, input); + VarOrReference var(shContext, input); return varAsObjectChecked(var.get(), ShardsTypes::Texture); } else { checkType(input.valueType, SHType::Table, "Attachment"); diff --git a/shards/modules/gfx/window.cpp b/shards/modules/gfx/window.cpp index 5d8dc7392a4..457454ed22d 100644 --- a/shards/modules/gfx/window.cpp +++ b/shards/modules/gfx/window.cpp @@ -583,6 +583,60 @@ struct WindowFocused { SHVar activate(SHContext *shContext, const SHVar &input) { return Var(_requiredWindowContext->window->isFocused()); } }; +#if !SH_EMSCRIPTEN +struct DisplayRefreshRate { + static SHTypesInfo inputTypes() { return CoreInfo::NoneType; } + static SHTypesInfo outputTypes() { return CoreInfo::FloatType; } + + static SHOptionalString help() { + return SHCCSTR("Outputs the refresh rate of the display as determined by the operating system."); + } + static SHOptionalString inputHelp() { return DefaultHelpText::InputHelpIgnored; } + static SHOptionalString outputHelp() { return SHCCSTR("Outputs the refresh rate of the display as a float."); } + + PARAM_PARAMVAR(_window, "Window", "The window to get the refresh rate of.", + {CoreInfo::NoneType, Type::VariableOf(WindowContext::Type)}); + PARAM_IMPL(PARAM_IMPL_FOR(_window)); + + OptionalWindowContext _optionalWindowContext; + + PARAM_REQUIRED_VARIABLES(); + SHTypeInfo compose(SHInstanceData &data) { + PARAM_COMPOSE_REQUIRED_VARIABLES(data); + _optionalWindowContext.compose(data, _requiredVariables, &_window); + return outputTypes().elements[0]; + } + + void warmup(SHContext *context) { + PARAM_WARMUP(context); + _optionalWindowContext.warmup(context, &_window); + } + + void cleanup(SHContext *context) { + PARAM_CLEANUP(context); + _optionalWindowContext.cleanup(); + } + + SHVar activate(SHContext *shContext, const SHVar &input) { + SDL_DisplayID display; + if (_optionalWindowContext) { + display = SDL_GetDisplayForWindow(_optionalWindowContext->window->window); + } else { + int displayCount{}; + auto displays = SDL_GetDisplays(&displayCount); + if (displayCount == 0) { + return Var(0.0f); + } + display = displays[0]; + } + + auto displayMode = SDL_GetCurrentDisplayMode(display); + return Var(displayMode->refresh_rate); + } +}; +void registerSDLWindowShards() { REGISTER_SHARD("GFX.DisplayRefreshRate", DisplayRefreshRate); } +#endif + void registerMainWindowShards() { REGISTER_SHARD("GFX.MainWindow", MainWindow); REGISTER_SHARD("GFX.WindowSize", WindowSize); @@ -592,6 +646,9 @@ void registerMainWindowShards() { REGISTER_SHARD("GFX.MoveWindow", MoveWindow); REGISTER_SHARD("GFX.WindowInsets", WindowInsets); REGISTER_SHARD("GFX.WindowFocused", WindowFocused); +#if !SH_EMSCRIPTEN + registerSDLWindowShards(); +#endif } } // namespace gfx diff --git a/shards/modules/http/http.cpp b/shards/modules/http/http.cpp index f73630a185e..a0d72bd37e8 100644 --- a/shards/modules/http/http.cpp +++ b/shards/modules/http/http.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #if !SH_EMSCRIPTEN #define BOOST_ERROR_CODE_HEADER_ONLY @@ -21,6 +22,12 @@ namespace http = beast::http; // from namespace net = boost::asio; // from using tcp = net::ip::tcp; // from +namespace boost::beast::http { +inline auto format_as(const verb& v) { + return magic_enum::enum_name(v); +} +} + #include #include #include diff --git a/shards/modules/physics/CMakeLists.txt b/shards/modules/physics/CMakeLists.txt index a88a4d6d88a..22d35caee25 100644 --- a/shards/modules/physics/CMakeLists.txt +++ b/shards/modules/physics/CMakeLists.txt @@ -11,12 +11,14 @@ add_shards_module(physics SOURCES ${physics_SOURCES} REGISTER_SHARDS physics shapes debug body soft_body constraints) -target_link_libraries(shards-module-physics - Jolt - gfx-core # For glTF interface -) +target_link_libraries(shards-module-physics Jolt shards-fast-string) + +if(TARGET wgpu-headers) + target_link_libraries(shards-module-physics wgpu-headers shards-fast-string) +endif() target_include_directories(shards-module-physics PUBLIC ${SHARDS_DIR}/deps/JoltPhysics/TestFramework PUBLIC ${SHARDS_DIR}/deps/JoltPhysics/Samples + PRIVATE ${SHARDS_DIR}/deps/JoltPhysics/Samples ) diff --git a/shards/modules/physics/body.cpp b/shards/modules/physics/body.cpp index f544706ac78..e1b47722e77 100644 --- a/shards/modules/physics/body.cpp +++ b/shards/modules/physics/body.cpp @@ -5,8 +5,7 @@ #include #include #include -#include -#include +#include #include #include "physics.hpp" #include "core.hpp" diff --git a/shards/modules/physics/constraints.cpp b/shards/modules/physics/constraints.cpp index c22847932e6..70349c12603 100644 --- a/shards/modules/physics/constraints.cpp +++ b/shards/modules/physics/constraints.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include namespace shards::Physics { diff --git a/shards/modules/physics/debug.cpp b/shards/modules/physics/debug.cpp index 7ef3af20288..df7f4c40990 100644 --- a/shards/modules/physics/debug.cpp +++ b/shards/modules/physics/debug.cpp @@ -1,11 +1,13 @@ +#include +#include +#if SHARDS_WITH_GFX #include #include #include #include -#include -#include -#include -#include +#include +#include +#include #include #include #include "core.hpp" @@ -116,8 +118,14 @@ struct DebugRenderer : public JPH::DebugRenderer { } }; -DECL_ENUM_INFO(JPH::BodyManager::EShapeColor, PhysicsDebugShapeColor, "Specifies colors for debug rendering of physics shapes. Used to visually distinguish different types of physics objects during debugging.", 'phDc'); -DECL_ENUM_INFO(JPH::ESoftBodyConstraintColor, PhysicsDebugSoftBodyConstraintColor, "Defines colors for debug rendering of soft body constraints. Helps visualize the internal structure and behavior of soft bodies in physics simulations.", 'phSd'); +DECL_ENUM_INFO(JPH::BodyManager::EShapeColor, PhysicsDebugShapeColor, + "Specifies colors for debug rendering of physics shapes. Used to visually distinguish different types of physics " + "objects during debugging.", + 'phDc'); +DECL_ENUM_INFO(JPH::ESoftBodyConstraintColor, PhysicsDebugSoftBodyConstraintColor, + "Defines colors for debug rendering of soft body constraints. Helps visualize the internal structure and behavior " + "of soft bodies in physics simulations.", + 'phSd'); struct DebugDrawShard { static SHTypesInfo inputTypes() { return shards::CoreInfo::AnyType; } @@ -149,7 +157,8 @@ struct DebugDrawShard { {CoreInfo::BoolType, CoreInfo::BoolVarType}); PARAM_PARAMVAR(_ds_shapeColor, "DrawBodyShapeColor", "Coloring scheme to use for shapes", {PhysicsDebugShapeColorEnumInfo::Type, Type::VariableOf(PhysicsDebugShapeColorEnumInfo::Type)}); - PARAM_PARAMVAR(_ds_boundingBox, "DrawBodyBoundingBox", "Draw a bounding box per body", {CoreInfo::BoolType, CoreInfo::BoolVarType}); + PARAM_PARAMVAR(_ds_boundingBox, "DrawBodyBoundingBox", "Draw a bounding box per body", + {CoreInfo::BoolType, CoreInfo::BoolVarType}); PARAM_PARAMVAR(_ds_centerOfMassTransform, "DrawBodyCenterOfMassTransform", "Draw the center of mass for each body", {CoreInfo::BoolType, CoreInfo::BoolVarType}); PARAM_PARAMVAR(_ds_worldTransform, "DrawBodyWorldTransform", @@ -296,10 +305,13 @@ struct DebugDrawShard { }; } // namespace shards::Physics +#endif SHARDS_REGISTER_FN(debug) { +#if SHARDS_WITH_GFX using namespace shards::Physics; REGISTER_ENUM(PhysicsDebugShapeColorEnumInfo); REGISTER_ENUM(PhysicsDebugSoftBodyConstraintColorEnumInfo); REGISTER_SHARD("Physics.DebugDraw", DebugDrawShard); +#endif } \ No newline at end of file diff --git a/shards/modules/physics/physics.cpp b/shards/modules/physics/physics.cpp index b448d66e44e..9f30eff2f5b 100644 --- a/shards/modules/physics/physics.cpp +++ b/shards/modules/physics/physics.cpp @@ -5,14 +5,14 @@ #include #include #include -#include -#include +#include #include #include "physics.hpp" #include "core.hpp" #include #include +#include namespace shards::Physics { std::atomic_uint64_t BodyNode::UidCounter = 0; @@ -757,4 +757,4 @@ SHARDS_REGISTER_FN(physics) { REGISTER_SHARD("Physics.ApplyImpulse", ApplyImpulse); REGISTER_SHARD("Physics.ApplyForce", ApplyForce); REGISTER_SHARD("Physics.ApplyForceAt", ApplyForceAt); -} \ No newline at end of file +} diff --git a/shards/modules/physics/shapes.cpp b/shards/modules/physics/shapes.cpp index e292297937f..b31a7189327 100644 --- a/shards/modules/physics/shapes.cpp +++ b/shards/modules/physics/shapes.cpp @@ -1,11 +1,14 @@ -#include "../gfx/shards_types.hpp" #include #include #include +#include #include "physics.hpp" -#include -#include +#if SHARDS_WITH_GFX +#include "../gfx/shards_types.hpp" +#include +#include +#endif #include #include @@ -22,7 +25,9 @@ static auto logger = getLogger(); struct BoxShapeShard { static SHTypesInfo inputTypes() { return CoreInfo::Float3Type; } static SHTypesInfo outputTypes() { return SHShape::Type; } - static SHOptionalString help() { return SHCCSTR("This shard creates a box collision shape from the input half extents provided."); } + static SHOptionalString help() { + return SHCCSTR("This shard creates a box collision shape from the input half extents provided."); + } static SHOptionalString inputHelp() { return SHCCSTR("The x,y and z half extents of the box collision shape to create."); } static SHOptionalString outputHelp() { return SHCCSTR("Outputs the created box collision shape."); } @@ -96,7 +101,9 @@ struct CapsuleShapeShard { static SHTypesInfo inputTypes() { return CoreInfo::AnyType; } static SHTypesInfo outputTypes() { return SHShape::Type; } static SHOptionalString help() { - return SHCCSTR("This shard creates a capsule physics collision shape, using the height and radius provided in the HalfHeight and Radius parameters respectively. The capsule will be centered around the origin with one sphere cap at (0, -HalfHeight, 0) and the " + return SHCCSTR("This shard creates a capsule physics collision shape, using the height and radius provided in the HalfHeight " + "and Radius parameters respectively. The capsule will be centered around the origin with one sphere cap at " + "(0, -HalfHeight, 0) and the " "other at (0, HalfHeight, 0)."); } static SHOptionalString inputHelp() { return DefaultHelpText::InputHelpIgnored; } @@ -134,6 +141,7 @@ struct CapsuleShapeShard { } }; +#if SHARDS_WITH_GFX struct MeshSrcTypes { static inline shards::Types Types{gfx::ShardsTypes::Mesh, gfx::ShardsTypes::Drawable}; }; @@ -241,7 +249,9 @@ struct MeshHullShapeShard { static SHOptionalString outputHelp() { return SHCCSTR("Outputs the created physics collisionshape."); } // See "Convex Radius" in https://jrouwe.github.io/JoltPhysics/index.html - PARAM_PARAMVAR(_maxConvexRadius, "MaxConvexRadius", "The convex radius given to the collision shape. A larger convex radius results in better performance but a less accurate simulation. A convex radius of 0 is allowed", + PARAM_PARAMVAR(_maxConvexRadius, "MaxConvexRadius", + "The convex radius given to the collision shape. A larger convex radius results in better performance but a " + "less accurate simulation. A convex radius of 0 is allowed", {shards::CoreInfo::FloatType, shards::CoreInfo::FloatVarType}); PARAM_IMPL(PARAM_IMPL_FOR(_maxConvexRadius)); @@ -277,6 +287,7 @@ struct MeshHullShapeShard { return (_output = std::move(var)); } }; +#endif } // namespace shards::Physics @@ -286,5 +297,7 @@ SHARDS_REGISTER_FN(shapes) { REGISTER_SHARD("Physics.BoxShape", BoxShapeShard); REGISTER_SHARD("Physics.SphereShape", SphereShapeShard); REGISTER_SHARD("Physics.CapsuleShape", CapsuleShapeShard); +#if SHARDS_WITH_GFX REGISTER_SHARD("Physics.HullShape", MeshHullShapeShard); +#endif } diff --git a/shards/modules/physics/soft_body.cpp b/shards/modules/physics/soft_body.cpp index b4ed0be82c5..646a384b941 100644 --- a/shards/modules/physics/soft_body.cpp +++ b/shards/modules/physics/soft_body.cpp @@ -2,9 +2,12 @@ #include #include #include -#include -#include +#include +#if SHARDS_WITH_GFX +#include +#include #include "../gfx/shards_types.hpp" +#endif #include "physics.hpp" #include "core.hpp" @@ -268,6 +271,7 @@ struct SoftBodyShard { } }; +#if SHARDS_WITH_GFX struct SoftBodyBuilder { JPH::Ref settings; @@ -436,10 +440,13 @@ struct SoftBodyShape { return (_output = std::move(var)); } }; +#endif } // namespace shards::Physics SHARDS_REGISTER_FN(soft_body) { using namespace shards::Physics; REGISTER_SHARD("Physics.SoftBody", SoftBodyShard); +#if SHARDS_WITH_GFX REGISTER_SHARD("Physics.SoftBodyShape", SoftBodyShape); +#endif } diff --git a/shards/modules/run/run.cpp b/shards/modules/run/run.cpp index f96b0a78eef..900f4a12ee5 100644 --- a/shards/modules/run/run.cpp +++ b/shards/modules/run/run.cpp @@ -154,7 +154,7 @@ struct Run { } } - SHLOG_DEBUG("Mesh (detached) is done, terminating, without errors: {}", noErrors); + SHLOG_DEBUG("Mesh (detached) is done, terminating, without errors: {}", (bool)noErrors); // Terminate the mesh mesh->terminate(); diff --git a/shards/rust/src/types.rs b/shards/rust/src/types.rs index db52d939c2b..a974dd097f7 100644 --- a/shards/rust/src/types.rs +++ b/shards/rust/src/types.rs @@ -3112,7 +3112,7 @@ macro_rules! ref_counted_object_type_impl { let rc = arg1 as *mut shards::types::RefCounted<$type>; (*rc).inc_ref(); } - + unsafe extern "C" fn release(arg1: *mut std::os::raw::c_void) { let rc = arg1 as *mut shards::types::RefCounted<$type>; (*rc).dec_ref(); diff --git a/shards/tests/defs.shs b/shards/tests/defs.shs index 9d54a2fc5c7..23646993651 100644 --- a/shards/tests/defs.shs +++ b/shards/tests/defs.shs @@ -1,21 +1,3 @@ -@macro(if [cond yes no] { - @template(filter-ast [] { - If({ExpectTable | Take("shs") | IsSeq} { - ExpectTable | Take("shs") - } { - = in - [[in]] - } - ) - }) - cond | If(IsAny([true "true" 1]) { - @ast(yes) | FromJson | @filter-ast() - } { - @ast(no) | FromJson | @filter-ast() - } - ) | ToJson -}) - @macro(tmp-path [path] { Sequence(pipelines) @platform | If(Is("emscripten") {