diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 1faed7036d..b6ceb1b478 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -53,7 +53,8 @@ jobs: genhtml \ coverage/coverage.linux.info \ coverage/coverage-macos-gpu.info \ - --ignore-errors source \ + --ignore-errors inconsistent,source,unmapped \ + --synthesize-missing \ --output-directory coverage/output - name: Upload report if: ${{ steps.setup.outputs.html-report == 'true' }} diff --git a/.github/workflows/test-macos-gpu.yml b/.github/workflows/test-macos-gpu.yml index c0bb310765..121407e638 100644 --- a/.github/workflows/test-macos-gpu.yml +++ b/.github/workflows/test-macos-gpu.yml @@ -175,7 +175,7 @@ jobs: --output-file coverage/coverage-macos-gpu.info \ --ignore-errors inconsistent,gcov,range # convert absolute path to relative path - sed -e "s|${PWD}/|./|g" coverage/coverage-macos-gpu.info + sed -i '' -e "s#${PWD}/#./#g" coverage/coverage-macos-gpu.info - name: Upload coverage (Debug) if: ${{ steps.setup.outputs.build-type == 'Debug' }} uses: actions/upload-artifact@v3 diff --git a/include/shards/utility.hpp b/include/shards/utility.hpp index dbedb92bc4..fa1ad558df 100644 --- a/include/shards/utility.hpp +++ b/include/shards/utility.hpp @@ -84,9 +84,9 @@ template struct constant { constexpr static decltype(V) value = V; }; -inline SHOptionalString operator"" _optional(const char *s, size_t) { return SHOptionalString{s}; } -inline SHStringWithLen operator"" _swl(const char *s, size_t l) { return SHStringWithLen{s, l}; } -inline std::string_view operator"" _sv(const char *s, size_t l) { return std::string_view{s, l}; } +inline SHOptionalString operator""_optional(const char *s, size_t) { return SHOptionalString{s}; } +inline SHStringWithLen operator""_swl(const char *s, size_t l) { return SHStringWithLen{s, l}; } +inline std::string_view operator""_sv(const char *s, size_t l) { return std::string_view{s, l}; } constexpr std::size_t StrLen(const char *str) { std::size_t len = 0; diff --git a/shards/core/async.hpp b/shards/core/async.hpp index 1bdea1318a..7fdacabcd9 100644 --- a/shards/core/async.hpp +++ b/shards/core/async.hpp @@ -76,11 +76,12 @@ struct TidePool { Worker(boost::lockfree::queue &queue, std::atomic_size_t &counter, std::mutex &condMutex, std::condition_variable &cond) : _queue(queue), _counter(counter), _condMutex(condMutex), _cond(cond) { + using namespace shards::literals; _running = true; boost::thread::attributes attrs; attrs.set_stack_size(SH_STACK_SIZE); _thread = boost::thread(attrs, [this]() { - pushThreadName("TidePool worker"); + pushThreadName("TidePool worker"_ns); while (_running) { Work *work{}; if (_queue.pop(work)) { @@ -142,7 +143,9 @@ struct TidePool { } void controllerWorker() { - pushThreadName("TidePool controller"); + using namespace shards::literals; + + pushThreadName("TidePool controller"_ns); // spawn workers first for (size_t i = 0; i < NumWorkers; ++i) { diff --git a/shards/core/foundation.hpp b/shards/core/foundation.hpp index 8eb4d39284..f22acba16d 100644 --- a/shards/core/foundation.hpp +++ b/shards/core/foundation.hpp @@ -19,6 +19,7 @@ #include "type_matcher.hpp" #include "type_info.hpp" #include "trait.hpp" +#include "utils.hpp" #include #include @@ -515,20 +516,20 @@ struct SHWire : public std::enable_shared_from_this { #if SH_DEBUG_THREAD_NAMES struct ThreadNameStrings { - ThreadNameStrings& init(SHWire *wire) { + ThreadNameStrings &init(SHWire *wire) { if (!initialized) { - resumeStr = fmt::format("Wire \"{}\"", (wire)->name); - extResumeStr = fmt::format(" \"{}\"", (wire)->name); - - suspendedStr= fmt::format(" \"{}\"", wire->name); + resumeStr = shards::NativeString{fmt::format("Wire \"{}\"", (wire)->name)}; + extResumeStr = shards::NativeString{fmt::format(" \"{}\"", (wire)->name)}; + + suspendedStr = shards::NativeString{fmt::format(" \"{}\"", wire->name)}; initialized = true; } return *this; } bool initialized = false; - std::string resumeStr; - std::string extResumeStr; - std::string suspendedStr; + shards::NativeString resumeStr; + shards::NativeString extResumeStr; + shards::NativeString suspendedStr; } threadNameStrings; #endif }; diff --git a/shards/core/taskflow.cpp b/shards/core/taskflow.cpp index e955fd68e3..6450228e0b 100644 --- a/shards/core/taskflow.cpp +++ b/shards/core/taskflow.cpp @@ -6,7 +6,7 @@ namespace shards { struct TaskFlowDebugInterface : tf::WorkerInterface { std::string debugName; #if SH_DEBUG_THREAD_NAMES - std::list debugThreadNameStack; + std::list debugThreadNameStack; #endif TaskFlowDebugInterface(std::string debugName) : debugName(debugName) {} diff --git a/shards/core/utils.cpp b/shards/core/utils.cpp index 8af816ecab..de9bf14d67 100644 --- a/shards/core/utils.cpp +++ b/shards/core/utils.cpp @@ -1,8 +1,8 @@ #include "utils.hpp" namespace shards { -thread_local std::list *_debugThreadStack; -std::list &getThreadNameStack() { - thread_local std::list stack; +thread_local std::list *_debugThreadStack; +std::list &getThreadNameStack() { + thread_local std::list stack; return stack; } } // namespace shards \ No newline at end of file diff --git a/shards/core/utils.hpp b/shards/core/utils.hpp index ac7b54cb8d..ad5cfe7c17 100644 --- a/shards/core/utils.hpp +++ b/shards/core/utils.hpp @@ -25,6 +25,11 @@ namespace shards { +template struct conststr { + char value[N]; + constexpr conststr(const char (&str)[N]) { std::copy_n(str, N, value); } +}; + #if SH_WINDOWS inline std::wstring toWindowsWString(std::string_view utf8) { std::wstring result; @@ -34,32 +39,85 @@ inline std::wstring toWindowsWString(std::string_view utf8) { } #endif -inline void setThreadName(std::string_view name_sv) { +struct NativeString { +#if SH_WINDOWS + NativeString() = default; + NativeString(std::string_view name) : name(toWindowsWString(name)) {} + NativeString &operator=(const std::string_view &name) { + this->name = toWindowsWString(name); + return *this; + } + ~NativeString() {} + std::wstring name; + using value_type = std::wstring; +#else + NativeString() = default; + NativeString(std::string_view name) : name(name) {} + NativeString &operator=(const std::string_view &name) { + this->name = name; + return *this; + } + ~NativeString() {} + std::string name; + using value_type = std::string; +#endif +}; #if SH_WINDOWS - std::wstring name = toWindowsWString(name_sv); - SetThreadDescription(GetCurrentThread(), name.c_str()); +using NativeStrType = std::wstring; +using NativeStrViewType = std::wstring_view; +#else +using NativeStrType = std::string; +using NativeStrViewType = std::string_view; +#endif +using NativeCStrType = const NativeStrType::value_type *; + +namespace detail { +template struct ConstNativeStringHolder { + static inline NativeString v; + static const auto &get() { + if (v.name.empty()) { + v = Name.value; + } + return v.name; + } +}; +struct ConstNativeStringValue { + const NativeStrType &name; +}; +} // namespace detail + +namespace literals { +template constexpr auto operator""_ns() { + return detail::ConstNativeStringValue{detail::ConstNativeStringHolder::get()}; +} +} // namespace literals + +// NOTE: Should be null terminated +inline void setThreadName(NativeCStrType v) { +#if SH_WINDOWS + SetThreadDescription(GetCurrentThread(), v); #elif SH_LINUX - std::string name{name_sv}; - pthread_setname_np(pthread_self(), name.c_str()); + pthread_setname_np(pthread_self(), v); #elif SH_APPLE - std::string name{name_sv}; - pthread_setname_np(name.c_str()); + pthread_setname_np(v); #endif } -std::list &getThreadNameStack(); +std::list &getThreadNameStack(); #if SH_DEBUG_THREAD_NAMES -inline void pushThreadNameConst(std::string_view name) { +// Use _ns suffix after constant +inline void pushThreadName(const detail::ConstNativeStringValue &v) { auto &stack = getThreadNameStack(); - stack.emplace_back(name.data()); - setThreadName(name); + stack.emplace_back(v.name); + setThreadName(v.name.c_str()); } - // NOTE: by reference, since you should keep the string alive for the duration of the thread -inline void pushThreadName(std::string &name) { pushThreadNameConst(name); } - -template inline void pushThreadName(const char (&str)[N]) { pushThreadNameConst(std::string_view(str, N)); } +inline void pushThreadName(NativeString &str) { + auto &stack = getThreadNameStack(); + stack.emplace_back(str.name); + setThreadName(str.name.c_str()); +} #else template inline void pushThreadName(const T &v) {} #endif @@ -67,7 +125,7 @@ template inline void pushThreadName(const T &v) {} #if SH_DEBUG_THREAD_NAMES // You can add this to the debugger watch window (shards::_debugThreadStack) // to see the current thread stack -extern thread_local std::list *_debugThreadStack; +extern thread_local std::list *_debugThreadStack; #endif inline void popThreadName() { @@ -76,7 +134,8 @@ inline void popThreadName() { _debugThreadStack = &stack; shassert(stack.size() > 0); stack.pop_back(); - setThreadName(stack.size() > 0 ? stack.back() : "Unnamed thread"); + static NativeString unnamed{"Unnamed thread"}; + setThreadName(stack.size() > 0 ? stack.back().data() : unnamed.name.data()); #endif } } // namespace shards diff --git a/shards/gfx/geom.cpp b/shards/gfx/geom.cpp index e81b4082e5..256409e70d 100644 --- a/shards/gfx/geom.cpp +++ b/shards/gfx/geom.cpp @@ -76,7 +76,7 @@ void SphereGenerator::generate() { // normal vertex.setNormal(linalg::normalize(position)); - vertex.setTexCoord(float2(1.0 - (u + uOffset), v)); + vertex.setTexCoord(float2(u + uOffset, v)); verticesRow.push_back(index++); } @@ -131,7 +131,11 @@ void PlaneGenerator::generate() { VertexPNT &vertex = vertices.emplace_back(); vertex.setPosition(float3(x, -y, 0)); vertex.setNormal(float3(0, 0, 1)); - vertex.setTexCoord(float2(float(ix) / gridX, 1 - (float(iy) / gridY))); + if(flipTextureVertically) { + vertex.setTexCoord(float2(float(ix) / gridX, 1 - float(iy) / gridY)); + } else { + vertex.setTexCoord(float2(float(ix) / gridX, float(iy) / gridY)); + } } } @@ -194,7 +198,7 @@ void CubeGenerator::generate() { float2 uv; uv.x = (ix / gridX); - uv.y = (1 - (iy / gridY)); + uv.y = (iy / gridY); vertex.setTexCoord(uv); } } @@ -341,7 +345,7 @@ void CylinderGenerator::generate() { float3 normal = linalg::normalize(float3(sinTheta, slope, cosTheta)); vertex.setNormal(normal); - vertex.setTexCoord(float2(u, 1 - v)); + vertex.setTexCoord(float2(u, v)); indexRow.emplace_back(index++); } diff --git a/shards/gfx/geom.hpp b/shards/gfx/geom.hpp index f15d9fe31b..077d248ddb 100644 --- a/shards/gfx/geom.hpp +++ b/shards/gfx/geom.hpp @@ -51,6 +51,9 @@ struct PlaneGenerator : public GeneratorBase { float height = 1; size_t widthSegments = 1; size_t heightSegments = 1; + // Flip to match UI space (where Y is down, X is right) + // otherwise it's Y up, X right + bool flipTextureVertically = false; void generate(); }; diff --git a/shards/gfx/tests/renderer_utils.hpp b/shards/gfx/tests/renderer_utils.hpp index d61af305d6..334510447a 100644 --- a/shards/gfx/tests/renderer_utils.hpp +++ b/shards/gfx/tests/renderer_utils.hpp @@ -63,7 +63,7 @@ inline MeshPtr createSphereMesh() { } inline MeshPtr createPlaneMesh() { - geom::PlaneGenerator gen; + geom::PlaneGenerator gen{.flipTextureVertically = true}; gen.generate(); return createMesh(gen.vertices, gen.indices); } diff --git a/shards/mal/CMakeLists.txt b/shards/mal/CMakeLists.txt index a31518811d..b52883d78b 100644 --- a/shards/mal/CMakeLists.txt +++ b/shards/mal/CMakeLists.txt @@ -15,11 +15,13 @@ function(setup_shards_target TARGET) if(NOT EMSCRIPTEN) target_link_libraries(${TARGET} Boost::process) endif() + if(APPLE) target_link_libraries(${TARGET} replxx shards-core shards_core_swift) else() target_link_libraries(${TARGET} replxx shards-core) endif() + set_target_properties(${TARGET} PROPERTIES LINKER_LANGUAGE CXX) # Need the lang-ffi bindings @@ -64,7 +66,7 @@ endif() if(APPLE) add_library(shards-framework SHARED ${shards_SOURCES}) setup_shards_target(shards-framework) - + # Framework specific settings set_target_properties(shards-framework PROPERTIES FRAMEWORK TRUE @@ -77,6 +79,7 @@ if(APPLE) XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" XCODE_ATTRIBUTE_SWIFT_INSTALL_OBJC_HEADER "NO" OUTPUT_NAME "Shards" + # Debug symbols generation XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym" XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS "YES" @@ -93,7 +96,7 @@ if(APPLE) INSTALL_RPATH "@executable_path/../Frameworks" BUILD_WITH_INSTALL_RPATH TRUE ) - + # Link Swift runtime for MacOS target_link_libraries(shards-framework "-framework Foundation" diff --git a/shards/mal/stepA_mal.cpp b/shards/mal/stepA_mal.cpp index af4b7e80fb..8655098f8d 100644 --- a/shards/mal/stepA_mal.cpp +++ b/shards/mal/stepA_mal.cpp @@ -106,11 +106,11 @@ int malmain(int argc, const char *argv[]) { } #ifndef NO_MAL_MAIN - +using namespace shards::literals; int main(int argc, const char *argv[]) { shards::parseArguments(argc, argv); - shards::pushThreadName("Main Thread"); + shards::pushThreadName("Main Thread"_ns); auto result = shards_process_args(argc, const_cast(argv), false); if (result != 99) // 99 triggers our old main diff --git a/shards/modules/core/wires.cpp b/shards/modules/core/wires.cpp index 524e84c2eb..2e6dd6ce2c 100644 --- a/shards/modules/core/wires.cpp +++ b/shards/modules/core/wires.cpp @@ -1590,9 +1590,9 @@ struct ParallelBase : public CapturingSpawners { cref->mesh = mesh; #if SH_DEBUG_THREAD_NAMES - static thread_local std::string debugThreadName; - debugThreadName.clear(); - fmt::format_to(std::back_inserter(debugThreadName), "tf::Executor \"{}\" ({} idx: {})", cref->wire->name, context->currentWire()->name, idx); + static thread_local shards::NativeString debugThreadName; + debugThreadName.name.clear(); + fmt::format_to(std::back_inserter(debugThreadName.name), "tf::Executor \"{}\" ({} idx: {})", cref->wire->name, context->currentWire()->name, idx); pushThreadName(debugThreadName); DEFER({ popThreadName(); }); #endif diff --git a/shards/modules/http/http.cpp b/shards/modules/http/http.cpp index 0686caac9f..aa74ec98b7 100644 --- a/shards/modules/http/http.cpp +++ b/shards/modules/http/http.cpp @@ -23,7 +23,6 @@ using tcp = net::ip::tcp; // from #include #include -#include #include #include #else @@ -31,6 +30,7 @@ using tcp = net::ip::tcp; // from #include #endif +#include #include #include @@ -1302,6 +1302,10 @@ struct SendFile { SH_SUSPEND(context, 0.0); } + _response.body().close(); + _response.clear(); + _404_response.clear(); + return input; } diff --git a/shards/tests/data/RGBA-quad.glb b/shards/tests/data/RGBA-quad.glb new file mode 100644 index 0000000000..dc064079f1 Binary files /dev/null and b/shards/tests/data/RGBA-quad.glb differ diff --git a/shards/tests/gfx-texture.shs b/shards/tests/gfx-texture.shs index af4432b8a6..69998d2e2a 100644 --- a/shards/tests/gfx-texture.shs +++ b/shards/tests/gfx-texture.shs @@ -6,20 +6,20 @@ Once({ 0.0 >= time GFX.BuiltinMesh(Type: BuiltinMeshType::Cube) >= mesh - + ; Load texture - LoadImage("../../assets/ShardsLogo.png") >= image | - Log("Image") | - GFX.Texture >= texture | + LoadImage("../../assets/ShardsLogo.png") >= image + Log("Image") + GFX.Texture >= texture Log("Texture") - + GFX.DrawQueue >= queue - + ; Create render steps GFX.BuiltinFeature(Id: BuiltinFeatureId::Transform) >> features GFX.BuiltinFeature(Id: BuiltinFeatureId::BaseColor) >> features GFX.DrawablePass(Features: features Queue: queue) >> render-steps - + ; Create view {Position: @f3(0 0 8) Target: @f3(0 0 0)} | Math.LookAt >= view-transform GFX.View(View: view-transform) >= view @@ -43,21 +43,23 @@ @wire(test-formats { Once({ GFX.BuiltinMesh(Type: BuiltinMeshType::Plane) >= mesh - + GFX.BuiltinMesh(Type: BuiltinMeshType::Cube) >= mesh-0 + GFX.BuiltinMesh(Type: BuiltinMeshType::Sphere) >= mesh-1 + LoadImage("data/RGBA.png") | GFX.Texture >= texture-0 LoadImage("data/RGB.png") | GFX.Texture >= texture-1 LoadImage("../../assets/ShardsLogo.png") | GFX.Texture >= texture-2 - + GFX.DrawQueue >= queue - + ; Create render steps GFX.BuiltinFeature(Id: BuiltinFeatureId::Transform) >> features GFX.BuiltinFeature(Id: BuiltinFeatureId::BaseColor) >> features GFX.DrawablePass(Features: features Queue: queue - Outputs: [{Name: "color" Clear: @f4(1.0)}]) >> render-steps - + Outputs: [{Name: "color" Clear: @f4(1.0)}]) >> render-steps + ; Create view - {Position: @f3(0 0 8.0) Target: @f3(0 0 0)} | Math.LookAt >= view-transform + {Position: @f3(0 -1.0 12.0) Target: @f3(0 -1.0 0)} | Math.LookAt >= view-transform GFX.View(View: view-transform) >= view }) GFX.MainWindow( @@ -66,10 +68,19 @@ @f3(-2 0 0) | Math.Translation | GFX.Drawable(Mesh: mesh Params: {baseColorTexture: texture-0}) | GFX.Draw(queue) @f3(0 0 0) | Math.Translation | GFX.Drawable(Mesh: mesh Params: {baseColorTexture: texture-1}) | GFX.Draw(queue) @f3(2 0 0) | Math.Translation | GFX.Drawable(Mesh: mesh Params: {baseColorTexture: texture-2}) | GFX.Draw(queue) + + Animation.Timer = t + @f3(-2 -2.0 0) | Math.Translation + Math.MatMul((t | Mul(2.0) | Math.AxisAngleY | Math.Rotation)) + Math.MatMul((t | Mul(1.4) | Math.AxisAngleX | Math.Rotation)) + GFX.Drawable(Mesh: mesh-0 Params: {baseColorTexture: texture-0}) | GFX.Draw(queue) + t | Mul(2.0) | Math.AxisAngleY | Math.Rotation + Math.MatMul((@f3(0 -2.0 0) | Math.Translation)) | GFX.Drawable(Mesh: mesh-1 Params: {baseColorTexture: texture-0}) | GFX.Draw(queue) + @f3(2 -2.0 0) | Math.Translation | GFX.glTF("data/RGBA-quad.glb" WrapRootNode: true) | GFX.Draw(queue) GFX.Render(Steps: render-steps View: view) } ) } Looped: true) @schedule(root test-formats) -@run(root FPS: 120 Iterations: 100) | Assert.Is(true) \ No newline at end of file +@run(root FPS: 120 Iterations: 100) | Assert.Is(true) diff --git a/shards/tests/test_runtime.cpp b/shards/tests/test_runtime.cpp index f59d0c4d21..ee4855c1c6 100644 --- a/shards/tests/test_runtime.cpp +++ b/shards/tests/test_runtime.cpp @@ -12,7 +12,9 @@ #include #include #include -#include "shards/core/wire_doppelganger_pool.hpp" +#include + +using namespace shards::literals; #undef CHECK @@ -1714,7 +1716,7 @@ TEST_CASE("shards-lang") { } TEST_CASE("meshThreadTask") { - shards::pushThreadName("Main Thread"); + shards::pushThreadName("Main Thread"_ns); auto mesh = SHMesh::make(); auto currentThreadId = std::this_thread::get_id(); @@ -1803,7 +1805,7 @@ TEST_CASE("meshThreadTask") { } TEST_CASE("meshThreadTask-looped") { - shards::pushThreadName("Main Thread"); + shards::pushThreadName("Main Thread"_ns); auto mesh = SHMesh::make(); auto currentThreadId = std::this_thread::get_id();