diff --git a/WickedEngine/wiArchive.h b/WickedEngine/wiArchive.h index aed3326c2..3f25ed936 100644 --- a/WickedEngine/wiArchive.h +++ b/WickedEngine/wiArchive.h @@ -4,6 +4,7 @@ #include "wiVector.h" #include "wiColor.h" #include "wiGraphics.h" +#include "wiAtomic.h" #include @@ -469,6 +470,22 @@ namespace wi } return *this; } + template + inline Archive& operator>>(std::atomic& data) + { + T val; + (*this) >> val; + data = val; + return *this; + } + template + inline Archive& operator>>(wi::relaxed_atomic& data) + { + T val; + (*this) >> val; + data = val; + return *this; + } diff --git a/WickedEngine/wiAtomic.h b/WickedEngine/wiAtomic.h new file mode 100644 index 000000000..1a4583891 --- /dev/null +++ b/WickedEngine/wiAtomic.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +// MSVC does not have constexpr atomic load(), so for now +// we just test for that, in C++20 we could also use +// type traits to check +#if defined(_MSC_VER) && !defined(__clang__) + #define WI_ATOMIC_CONSTEXPR inline +#else + #define WI_ATOMIC_CONSTEXPR constexpr +#endif + +namespace wi { + + + // subclass of std::atomic that allows copying + // note that copying *is not* atomic; this is a helper class that allows us + // to copy classes/structs that contain atomic members where we can accept + // that copying is not atomic + template + struct copyable_atomic : public std::atomic { + copyable_atomic() noexcept = default; + constexpr copyable_atomic(T v) noexcept : std::atomic(v) {}; + copyable_atomic(const copyable_atomic& other) { + std::atomic::store(other.load()); + } + copyable_atomic& operator=(const copyable_atomic& other) { + std::atomic::store(other.load()); + return *this; + } + }; + + // simple wrapper that always defaults to memory_order_relaxed + template + struct relaxed_atomic { + std::atomic val; + + relaxed_atomic() noexcept = default; + constexpr relaxed_atomic(T v) noexcept : val(v) {}; + relaxed_atomic(const relaxed_atomic& other) : val(other.load(std::memory_order_relaxed)) {}; + relaxed_atomic& operator=(const relaxed_atomic& other) { + val.store(other.val.load(std::memory_order_relaxed), std::memory_order_relaxed); + return *this; + } + void store(T desired, std::memory_order order = std::memory_order_relaxed) { + return val.store(desired, order); + } + T load(std::memory_order order = std::memory_order_relaxed) const { + return val.load(order); + } + + T fetch_xor(T arg, std::memory_order order = std::memory_order_relaxed) { + return val.fetch_xor(arg, order); + } + T fetch_and(T arg, std::memory_order order = std::memory_order_relaxed) { + return val.fetch_add(arg, order); + } + T fetch_or(T arg, std::memory_order order = std::memory_order_relaxed) { + return val.fetch_or(arg, order); + } + + operator T() const noexcept { return load(); } + T operator=(T desired) noexcept { store(desired); return desired;} + T operator&=(T arg) { return fetch_and(arg); } + T operator|=(T arg) { return fetch_or(arg); } + T operator^=(T arg) { return fetch_xor(arg); } + }; + + +} diff --git a/WickedEngine/wiBacklog.cpp b/WickedEngine/wiBacklog.cpp index ad186ff56..4433fb6fe 100644 --- a/WickedEngine/wiBacklog.cpp +++ b/WickedEngine/wiBacklog.cpp @@ -36,7 +36,7 @@ namespace wi::backlog wi::font::Params font_params; wi::Color backgroundColor = wi::Color(17, 30, 43, 255); Texture backgroundTex; - bool refitscroll = false; + std::atomic refitscroll = false; wi::gui::TextInputField inputField; wi::gui::Button toggleButton; wi::gui::GUI GUI; @@ -44,7 +44,7 @@ namespace wi::backlog bool locked = false; bool blockLuaExec = false; LogLevel logLevel = LogLevel::Default; - LogLevel unseen = LogLevel::None; + std::atomic unseen = LogLevel::None; std::deque history; std::mutex historyLock; @@ -494,7 +494,7 @@ namespace wi::backlog wi::font::SetCanvas(canvas); // always set here as it can be called from outside... wi::font::Params params = font_params; params.cursor = {}; - if (refitscroll) + if (refitscroll.exchange(false, std::memory_order_relaxed)) { const float textheight = wi::font::TextHeight(getText(), params); const float limit = canvas.GetLogicalHeight() - 50; @@ -502,7 +502,6 @@ namespace wi::backlog { scroll = limit - textheight; } - refitscroll = false; } params.posX = 5; params.posY = pos + scroll; @@ -599,7 +598,7 @@ namespace wi::backlog // Enqueue for async file writing asyncWriter.Enqueue(str); - refitscroll = true; + refitscroll.store(true); switch (level) { @@ -615,7 +614,11 @@ namespace wi::backlog break; } - unseen = std::max(unseen, level); + // atomic version of unseen = max(unseen, level) + LogLevel current_unseen = unseen.load(std::memory_order_relaxed); + while (current_unseen < level) { + unseen.compare_exchange_weak(current_unseen, level, std::memory_order_acq_rel, std::memory_order_relaxed); + } // Force an immediate flush on errors to prevent potential data loss // in case the application is about to crash diff --git a/WickedEngine/wiECS.h b/WickedEngine/wiECS.h index 9bd93b848..1cba46b01 100644 --- a/WickedEngine/wiECS.h +++ b/WickedEngine/wiECS.h @@ -12,6 +12,7 @@ #include #include #include +#include #include // Entity-Component System @@ -604,14 +605,17 @@ namespace wi::ecs }; Item items[64]; }; + mutable std::shared_mutex mutex; wi::unordered_map table; inline void clear() { + std::unique_lock lock(mutex); table.clear(); } inline void erase(Entity entity) { + std::unique_lock lock(mutex); const uint64_t block_index = entity >> 6ull; // entity / 64 auto it = table.find(block_index); if (it == table.end()) @@ -631,6 +635,7 @@ namespace wi::ecs } inline void insert(Entity entity, size_t index) { + std::unique_lock lock(mutex); const uint64_t block_index = entity >> 6ull; // entity / 64 const uint64_t item_index = entity & 63ull; // entity % 64 Block& block = table[block_index]; @@ -639,6 +644,7 @@ namespace wi::ecs } inline size_t get(Entity entity) const { + std::shared_lock lock(mutex); const uint64_t block_index = entity >> 6ull; // entity / 64 const auto it = table.find(block_index); if (it == table.end()) @@ -653,21 +659,25 @@ namespace wi::ecs // Implementation with hash table: // The standard hashing method, performance depends on hashing, hash collisions wi::unordered_map table; - + mutable std::shared_mutex mutex; inline void clear() { + std::unique_lock lock(mutex); table.clear(); } inline void erase(Entity entity) { + std::unique_lock lock(mutex); table.erase(entity); } inline void insert(Entity entity, size_t index) { + std::unique_lock lock(mutex); table[entity] = index; } inline size_t get(Entity entity) const { + std::shared_lock lock(mutex); if (table.empty()) return INVALID_INDEX; auto it = table.find(entity); @@ -694,6 +704,7 @@ namespace wi::ecs uint64_t version = 0; }; wi::unordered_map entries; + mutable std::shared_mutex mutex; // Create an instance of ComponentManager of a certain data type // The name must be unique, it will be used in serialization @@ -701,6 +712,7 @@ namespace wi::ecs template inline ComponentManager& Register(const std::string& name, uint64_t version = 0) { + std::unique_lock lock(mutex); entries[name].component_manager = std::make_unique>(); entries[name].version = version; return static_cast&>(*entries[name].component_manager); @@ -709,6 +721,7 @@ namespace wi::ecs template inline ComponentManager* Get(const std::string& name) { + std::shared_lock lock(mutex); auto it = entries.find(name); if (it == entries.end()) return nullptr; @@ -718,6 +731,7 @@ namespace wi::ecs template inline const ComponentManager* Get(const std::string& name) const { + std::shared_lock lock(mutex); auto it = entries.find(name); if (it == entries.end()) return nullptr; @@ -726,6 +740,7 @@ namespace wi::ecs inline uint64_t GetVersion(std::string name) const { + std::shared_lock lock(mutex); auto it = entries.find(name); if (it == entries.end()) return 0; diff --git a/WickedEngine/wiGraphicsDevice.h b/WickedEngine/wiGraphicsDevice.h index cb78757e0..bce412885 100644 --- a/WickedEngine/wiGraphicsDevice.h +++ b/WickedEngine/wiGraphicsDevice.h @@ -3,6 +3,7 @@ #include "wiGraphics.h" #include "wiPlatform.h" +#include #include #include #include @@ -52,7 +53,7 @@ namespace wi::graphics { protected: static constexpr uint32_t BUFFERCOUNT = 2; - uint64_t FRAMECOUNT = 0; + std::atomic FRAMECOUNT = 0; size_t SHADER_IDENTIFIER_SIZE = 0; size_t TOPLEVEL_ACCELERATION_STRUCTURE_INSTANCE_SIZE = 0; uint64_t TIMESTAMP_FREQUENCY = 0; @@ -116,9 +117,15 @@ namespace wi::graphics // One PipelineState object can be compiled internally for multiple render target or depth-stencil formats, or sample counts virtual size_t GetActivePipelineCount() const = 0; + +#if defined(_MSC_VER) && !defined(__clang__) + #define constexpr_no_msvc +#else + #define constexpr_no_msvc constexpr +#endif // Returns the number of elapsed frames (submits) // It is incremented when calling SubmitCommandLists() - constexpr uint64_t GetFrameCount() const { return FRAMECOUNT; } + constexpr_no_msvc uint64_t GetFrameCount() const { return FRAMECOUNT; } // Check whether the graphics device supports a feature or not constexpr bool CheckCapability(GraphicsDeviceCapability capability) const { return has_flag(capabilities, capability); } @@ -126,7 +133,7 @@ namespace wi::graphics // Returns the buffer count, which is the array size of buffered resources used by both the CPU and GPU static constexpr uint32_t GetBufferCount() { return BUFFERCOUNT; } // Returns the current buffer index, which is in range [0, GetBufferCount() - 1] - constexpr uint32_t GetBufferIndex() const { return GetFrameCount() % GetBufferCount(); } + constexpr_no_msvc uint32_t GetBufferIndex() const { return GetFrameCount() % GetBufferCount(); } // Returns whether the graphics debug layer is enabled. It can be enabled when creating the device. constexpr bool IsDebugDevice() const { return validationMode != ValidationMode::Disabled; } @@ -294,7 +301,7 @@ namespace wi::graphics inline bool IsValid() const { return data != nullptr && buffer.IsValid(); } }; - // Allocates temporary memory that the CPU can write and GPU can read. + // Allocates temporary memory that the CPU can write and GPU can read. // It is only alive for one frame and automatically invalidated after that. GPUAllocation AllocateGPU(uint64_t dataSize, CommandList cmd) { diff --git a/WickedEngine/wiGraphicsDevice_DX12.cpp b/WickedEngine/wiGraphicsDevice_DX12.cpp index e1457b78f..8e7aa0376 100644 --- a/WickedEngine/wiGraphicsDevice_DX12.cpp +++ b/WickedEngine/wiGraphicsDevice_DX12.cpp @@ -5388,7 +5388,7 @@ std::mutex queue_locker; else { allocationhandler->destroylocker.lock(); - allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT)); + allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, GetFrameCount())); allocationhandler->destroylocker.unlock(); } } @@ -5767,7 +5767,7 @@ std::mutex queue_locker; for (auto& x : pipelines_global) { - allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT)); + allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, GetFrameCount())); } pipelines_global.clear(); @@ -5775,7 +5775,7 @@ std::mutex queue_locker; { for (auto& y : x->pipelines_worker) { - allocationhandler->destroyer_pipelines.push_back(std::make_pair(y.second, FRAMECOUNT)); + allocationhandler->destroyer_pipelines.push_back(std::make_pair(y.second, GetFrameCount())); } x->pipelines_worker.clear(); } diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.cpp b/WickedEngine/wiGraphicsDevice_Vulkan.cpp index 0c611375b..c7f77cd82 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.cpp +++ b/WickedEngine/wiGraphicsDevice_Vulkan.cpp @@ -1635,7 +1635,7 @@ using namespace vulkan_internal; if (descriptorPool != VK_NULL_HANDLE) { device->allocationhandler->destroylocker.lock(); - device->allocationhandler->destroyer_descriptorPools.push_back(std::make_pair(descriptorPool, device->FRAMECOUNT)); + device->allocationhandler->destroyer_descriptorPools.push_back(std::make_pair(descriptorPool, device->GetFrameCount())); descriptorPool = VK_NULL_HANDLE; device->allocationhandler->destroylocker.unlock(); } @@ -7412,7 +7412,7 @@ using namespace vulkan_internal; else { allocationhandler->destroylocker.lock(); - allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT)); + allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, GetFrameCount())); allocationhandler->destroylocker.unlock(); } } @@ -7510,7 +7510,7 @@ using namespace vulkan_internal; for (auto& x : pipelines_global) { - allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT)); + allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, GetFrameCount())); } pipelines_global.clear(); @@ -7518,7 +7518,7 @@ using namespace vulkan_internal; { for (auto& y : x->pipelines_worker) { - allocationhandler->destroyer_pipelines.push_back(std::make_pair(y.second, FRAMECOUNT)); + allocationhandler->destroyer_pipelines.push_back(std::make_pair(y.second, GetFrameCount())); } x->pipelines_worker.clear(); } diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index 70693385c..6faa0f8c8 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -51,7 +51,7 @@ namespace wi::scene }; XMFLOAT3 scale_local = XMFLOAT3(1, 1, 1); - uint32_t _flags = DIRTY; + wi::copyable_atomic _flags = DIRTY; XMFLOAT4 rotation_local = XMFLOAT4(0, 0, 0, 1); // this is a quaternion XMFLOAT3 translation_local = XMFLOAT3(0, 0, 0); @@ -64,7 +64,7 @@ namespace wi::scene XMFLOAT4X4 world = wi::math::IDENTITY_MATRIX; constexpr void SetDirty(bool value = true) { if (value) { _flags |= DIRTY; } else { _flags &= ~DIRTY; } } - constexpr bool IsDirty() const { return _flags & DIRTY; } + WI_ATOMIC_CONSTEXPR bool IsDirty() const { return _flags & DIRTY; } XMFLOAT3 GetPosition() const; XMFLOAT4 GetRotation() const; @@ -140,7 +140,7 @@ namespace wi::scene DISABLE_CAPSULE_SHADOW = 1 << 17, INTERNAL = 1 << 18 // used only for internal purposes }; - uint32_t _flags = CAST_SHADOW; + wi::copyable_atomic _flags = CAST_SHADOW; enum SHADERTYPE { @@ -280,10 +280,10 @@ namespace wi::scene constexpr bool HasPlanarReflection() const { return shaderType == SHADERTYPE_PBR_PLANARREFLECTION || shaderType == SHADERTYPE_WATER; } constexpr void SetDirty(bool value = true) { if (value) { _flags |= DIRTY; } else { _flags &= ~DIRTY; } } - constexpr bool IsDirty() const { return _flags & DIRTY; } + WI_ATOMIC_CONSTEXPR bool IsDirty() const { return _flags & DIRTY; } constexpr void SetInternal(bool value = true) { if (value) { _flags |= INTERNAL; } else { _flags &= ~INTERNAL; } } - constexpr bool IsInternal() const { return _flags & INTERNAL; } + WI_ATOMIC_CONSTEXPR bool IsInternal() const { return _flags & INTERNAL; } constexpr void SetCastShadow(bool value) { SetDirty(); if (value) { _flags |= CAST_SHADOW; } else { _flags &= ~CAST_SHADOW; } } constexpr void SetReceiveShadow(bool value) { SetDirty(); if (value) { _flags &= ~DISABLE_RECEIVE_SHADOW; } else { _flags |= DISABLE_RECEIVE_SHADOW; } } @@ -292,21 +292,21 @@ namespace wi::scene wi::enums::BLENDMODE GetBlendMode() const { if (userBlendMode == wi::enums::BLENDMODE_OPAQUE && (GetFilterMask() & wi::enums::FILTER_TRANSPARENT)) return wi::enums::BLENDMODE_ALPHA; else return userBlendMode; } - constexpr bool IsCastingShadow() const { return _flags & CAST_SHADOW; } - constexpr bool IsAlphaTestEnabled() const { return alphaRef <= 1.0f - 1.0f / 256.0f; } - constexpr bool IsUsingVertexColors() const { return _flags & USE_VERTEXCOLORS; } - constexpr bool IsUsingWind() const { return _flags & USE_WIND; } - constexpr bool IsReceiveShadow() const { return (_flags & DISABLE_RECEIVE_SHADOW) == 0; } - constexpr bool IsUsingSpecularGlossinessWorkflow() const { return _flags & SPECULAR_GLOSSINESS_WORKFLOW; } - constexpr bool IsOcclusionEnabled_Primary() const { return _flags & OCCLUSION_PRIMARY; } - constexpr bool IsOcclusionEnabled_Secondary() const { return _flags & OCCLUSION_SECONDARY; } - constexpr bool IsCustomShader() const { return customShaderID >= 0; } - constexpr bool IsDoubleSided() const { return _flags & DOUBLE_SIDED; } - constexpr bool IsOutlineEnabled() const { return _flags & OUTLINE; } - constexpr bool IsPreferUncompressedTexturesEnabled() const { return _flags & PREFER_UNCOMPRESSED_TEXTURES; } - constexpr bool IsVertexAODisabled() const { return _flags & DISABLE_VERTEXAO; } - constexpr bool IsTextureStreamingDisabled() const { return _flags & DISABLE_TEXTURE_STREAMING; } - constexpr bool IsCoplanarBlending() const { return _flags & COPLANAR_BLENDING; } + WI_ATOMIC_CONSTEXPR bool IsCastingShadow() const { return _flags & CAST_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsAlphaTestEnabled() const { return alphaRef <= 1.0f - 1.0f / 256.0f; } + WI_ATOMIC_CONSTEXPR bool IsUsingVertexColors() const { return _flags & USE_VERTEXCOLORS; } + WI_ATOMIC_CONSTEXPR bool IsUsingWind() const { return _flags & USE_WIND; } + WI_ATOMIC_CONSTEXPR bool IsReceiveShadow() const { return (_flags & DISABLE_RECEIVE_SHADOW) == 0; } + WI_ATOMIC_CONSTEXPR bool IsUsingSpecularGlossinessWorkflow() const { return _flags & SPECULAR_GLOSSINESS_WORKFLOW; } + WI_ATOMIC_CONSTEXPR bool IsOcclusionEnabled_Primary() const { return _flags & OCCLUSION_PRIMARY; } + WI_ATOMIC_CONSTEXPR bool IsOcclusionEnabled_Secondary() const { return _flags & OCCLUSION_SECONDARY; } + WI_ATOMIC_CONSTEXPR bool IsCustomShader() const { return customShaderID >= 0; } + WI_ATOMIC_CONSTEXPR bool IsDoubleSided() const { return _flags & DOUBLE_SIDED; } + WI_ATOMIC_CONSTEXPR bool IsOutlineEnabled() const { return _flags & OUTLINE; } + WI_ATOMIC_CONSTEXPR bool IsPreferUncompressedTexturesEnabled() const { return _flags & PREFER_UNCOMPRESSED_TEXTURES; } + WI_ATOMIC_CONSTEXPR bool IsVertexAODisabled() const { return _flags & DISABLE_VERTEXAO; } + WI_ATOMIC_CONSTEXPR bool IsTextureStreamingDisabled() const { return _flags & DISABLE_TEXTURE_STREAMING; } + WI_ATOMIC_CONSTEXPR bool IsCoplanarBlending() const { return _flags & COPLANAR_BLENDING; } constexpr void SetBaseColor(const XMFLOAT4& value) { SetDirty(); baseColor = value; } constexpr void SetSpecularColor(const XMFLOAT4& value) { SetDirty(); specularColor = value; } @@ -377,7 +377,7 @@ namespace wi::scene interiorMappingRotation = value; } - constexpr bool IsCapsuleShadowDisabled() const { return _flags & DISABLE_CAPSULE_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsCapsuleShadowDisabled() const { return _flags & DISABLE_CAPSULE_SHADOW; } constexpr void SetCapsuleShadowDisabled(bool value = true) { if (value) { _flags |= DISABLE_CAPSULE_SHADOW; } else { _flags &= ~DISABLE_CAPSULE_SHADOW; } } constexpr void SetMeshBlend(float value) { mesh_blend = value; SetDirty(); } @@ -414,7 +414,7 @@ namespace wi::scene REFRESH_PARAMETERS_REQUEST = 1 << 3, CHARACTER_PHYSICS = 1 << 4, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; enum CollisionShape : uint32_t { @@ -530,18 +530,18 @@ namespace wi::scene constexpr void SetKinematic(bool value) { if (value) { _flags |= KINEMATIC; } else { _flags &= ~KINEMATIC; } } constexpr void SetStartDeactivated(bool value) { if (value) { _flags |= START_DEACTIVATED; } else { _flags &= ~START_DEACTIVATED; } } - constexpr bool IsVehicle() const { return vehicle.type != Vehicle::Type::None; } - constexpr bool IsCar() const { return vehicle.type == Vehicle::Type::Car; } - constexpr bool IsMotorcycle() const { return vehicle.type == Vehicle::Type::Motorcycle; } - constexpr bool IsDisableDeactivation() const { return _flags & DISABLE_DEACTIVATION; } - constexpr bool IsKinematic() const { return _flags & KINEMATIC; } - constexpr bool IsStartDeactivated() const { return _flags & START_DEACTIVATED; } + WI_ATOMIC_CONSTEXPR bool IsVehicle() const { return vehicle.type != Vehicle::Type::None; } + WI_ATOMIC_CONSTEXPR bool IsCar() const { return vehicle.type == Vehicle::Type::Car; } + WI_ATOMIC_CONSTEXPR bool IsMotorcycle() const { return vehicle.type == Vehicle::Type::Motorcycle; } + WI_ATOMIC_CONSTEXPR bool IsDisableDeactivation() const { return _flags & DISABLE_DEACTIVATION; } + WI_ATOMIC_CONSTEXPR bool IsKinematic() const { return _flags & KINEMATIC; } + WI_ATOMIC_CONSTEXPR bool IsStartDeactivated() const { return _flags & START_DEACTIVATED; } constexpr void SetRefreshParametersNeeded(bool value = true) { if (value) { _flags |= REFRESH_PARAMETERS_REQUEST; } else { _flags &= ~REFRESH_PARAMETERS_REQUEST; } } - constexpr bool IsRefreshParametersNeeded() const { return _flags & REFRESH_PARAMETERS_REQUEST; } + WI_ATOMIC_CONSTEXPR bool IsRefreshParametersNeeded() const { return _flags & REFRESH_PARAMETERS_REQUEST; } constexpr void SetCharacterPhysics(bool value = true) { if (value) { _flags |= CHARACTER_PHYSICS; } else { _flags &= ~CHARACTER_PHYSICS; } } - constexpr bool IsCharacterPhysics() const { return _flags & CHARACTER_PHYSICS; } + WI_ATOMIC_CONSTEXPR bool IsCharacterPhysics() const { return _flags & CHARACTER_PHYSICS; } void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; @@ -554,7 +554,7 @@ namespace wi::scene REFRESH_PARAMETERS_REQUEST = 1 << 0, DISABLE_SELF_COLLISION = 1 << 1, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; // Note: the constraint axes are taken from the TransformComponent on the constraint's entity // RIGHT axis means X axis in the default orientation @@ -641,11 +641,11 @@ namespace wi::scene // Request refreshing of constraint settings without recreating the constraint constexpr void SetRefreshParametersNeeded(bool value = true) { if (value) { _flags |= REFRESH_PARAMETERS_REQUEST; } else { _flags &= ~REFRESH_PARAMETERS_REQUEST; } } - constexpr bool IsRefreshParametersNeeded() const { return _flags & REFRESH_PARAMETERS_REQUEST; } + WI_ATOMIC_CONSTEXPR bool IsRefreshParametersNeeded() const { return _flags & REFRESH_PARAMETERS_REQUEST; } // Enable/disable collision between the two bodies that this constraint targets constexpr void SetDisableSelfCollision(bool value = true) { if (value) { _flags |= DISABLE_SELF_COLLISION; } else { _flags &= ~DISABLE_SELF_COLLISION; } } - constexpr bool IsDisableSelfCollision() const { return _flags & DISABLE_SELF_COLLISION; } + WI_ATOMIC_CONSTEXPR bool IsDisableSelfCollision() const { return _flags & DISABLE_SELF_COLLISION; } void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; @@ -666,7 +666,7 @@ namespace wi::scene BVH_ENABLED = 1 << 8, QUANTIZED_POSITIONS_DISABLED = 1 << 9, }; - // *uint32_t _flags is moved down for better struct padding... + // *wi::copyable_atomic _flags is moved down for better struct padding... wi::vector vertex_positions; wi::vector vertex_normals; @@ -698,7 +698,7 @@ namespace wi::scene uint32_t materialIndex = 0; uint32_t flags = 0; - constexpr bool IsDoubleSided() const { return flags & MESH_SUBSET_DOUBLESIDED; } + WI_ATOMIC_CONSTEXPR bool IsDoubleSided() const { return flags & MESH_SUBSET_DOUBLESIDED; } }; wi::vector subsets; @@ -736,7 +736,7 @@ namespace wi::scene int subresource_uav = -1; int descriptor_uav = -1; - constexpr bool IsValid() const + WI_ATOMIC_CONSTEXPR bool IsValid() const { return offset != ~0ull; } @@ -779,7 +779,7 @@ namespace wi::scene RigidBodyPhysicsComponent precomputed_rigidbody_physics_shape; // you can precompute a physics shape here if you need without using a real rigid body component yet - uint32_t _flags = RENDERABLE; // *this is serialized but put here for better struct padding + wi::copyable_atomic _flags = RENDERABLE; // *this is serialized but put here for better struct padding enum BLAS_STATE { @@ -803,15 +803,15 @@ namespace wi::scene // This should be enabled for connecting meshes like terrain chunks if their AABB is not consistent with each other constexpr void SetQuantizedPositionsDisabled(bool value) { if (value) { _flags |= QUANTIZED_POSITIONS_DISABLED; } else { _flags &= ~QUANTIZED_POSITIONS_DISABLED; } } - constexpr bool IsRenderable() const { return _flags & RENDERABLE; } - constexpr bool IsDoubleSided() const { return _flags & DOUBLE_SIDED; } - constexpr bool IsDoubleSidedShadow() const { return _flags & DOUBLE_SIDED_SHADOW; } - constexpr bool IsDynamic() const { return _flags & DYNAMIC; } - constexpr bool IsBVHEnabled() const { return _flags & BVH_ENABLED; } - constexpr bool IsQuantizedPositionsDisabled() const { return _flags & QUANTIZED_POSITIONS_DISABLED; } + WI_ATOMIC_CONSTEXPR bool IsRenderable() const { return _flags & RENDERABLE; } + WI_ATOMIC_CONSTEXPR bool IsDoubleSided() const { return _flags & DOUBLE_SIDED; } + WI_ATOMIC_CONSTEXPR bool IsDoubleSidedShadow() const { return _flags & DOUBLE_SIDED_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsDynamic() const { return _flags & DYNAMIC; } + WI_ATOMIC_CONSTEXPR bool IsBVHEnabled() const { return _flags & BVH_ENABLED; } + WI_ATOMIC_CONSTEXPR bool IsQuantizedPositionsDisabled() const { return _flags & QUANTIZED_POSITIONS_DISABLED; } constexpr float GetTessellationFactor() const { return tessellationFactor; } - constexpr bool IsSkinned() const { return armatureID != wi::ecs::INVALID_ENTITY; } + WI_ATOMIC_CONSTEXPR bool IsSkinned() const { return armatureID != wi::ecs::INVALID_ENTITY; } inline wi::graphics::IndexBufferFormat GetIndexFormat() const { return wi::graphics::GetIndexBufferFormat((uint32_t)vertex_positions.size()); } inline size_t GetIndexStride() const { return GetIndexFormat() == wi::graphics::IndexBufferFormat::UINT32 ? sizeof(uint32_t) : sizeof(uint16_t); } @@ -1126,7 +1126,7 @@ namespace wi::scene EMPTY = 0, DIRTY = 1 << 0, }; - uint32_t _flags = DIRTY; + wi::copyable_atomic _flags = DIRTY; float swapInDistance = 100.0f; @@ -1135,7 +1135,7 @@ namespace wi::scene int textureIndex = -1; constexpr void SetDirty(bool value = true) { if (value) { _flags |= DIRTY; } else { _flags &= ~DIRTY; } } - constexpr bool IsDirty() const { return _flags & DIRTY; } + WI_ATOMIC_CONSTEXPR bool IsDirty() const { return _flags & DIRTY; } void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; @@ -1157,7 +1157,7 @@ namespace wi::scene NOT_VISIBLE_IN_REFLECTIONS = 1 << 9, WETMAP_ENABLED = 1 << 10, }; - uint32_t _flags = RENDERABLE | CAST_SHADOW; + wi::copyable_atomic _flags = RENDERABLE | CAST_SHADOW; uint8_t userStencilRef = 0; wi::ecs::Entity meshID = wi::ecs::INVALID_ENTITY; @@ -1212,16 +1212,16 @@ namespace wi::scene constexpr void SetNotVisibleInReflections(bool value) { if (value) { _flags |= NOT_VISIBLE_IN_REFLECTIONS; } else { _flags &= ~NOT_VISIBLE_IN_REFLECTIONS; } } constexpr void SetWetmapEnabled(bool value) { if (value) { _flags |= WETMAP_ENABLED; } else { _flags &= ~WETMAP_ENABLED; } } - constexpr bool IsRenderable() const { return (_flags & RENDERABLE) && (GetTransparency() < 0.99f); } - constexpr bool IsCastingShadow() const { return _flags & CAST_SHADOW; } - constexpr bool IsDynamic() const { return _flags & DYNAMIC; } - constexpr bool IsRequestPlanarReflection() const { return _flags & REQUEST_PLANAR_REFLECTION; } - constexpr bool IsLightmapRenderRequested() const { return _flags & LIGHTMAP_RENDER_REQUEST; } - constexpr bool IsLightmapDisableBlockCompression() const { return _flags & LIGHTMAP_DISABLE_BLOCK_COMPRESSION; } - constexpr bool IsForeground() const { return _flags & FOREGROUND; } - constexpr bool IsNotVisibleInMainCamera() const { return _flags & NOT_VISIBLE_IN_MAIN_CAMERA; } - constexpr bool IsNotVisibleInReflections() const { return _flags & NOT_VISIBLE_IN_REFLECTIONS; } - constexpr bool IsWetmapEnabled() const { return _flags & WETMAP_ENABLED; } + WI_ATOMIC_CONSTEXPR bool IsRenderable() const { return (_flags & RENDERABLE) && (GetTransparency() < 0.99f); } + WI_ATOMIC_CONSTEXPR bool IsCastingShadow() const { return _flags & CAST_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsDynamic() const { return _flags & DYNAMIC; } + WI_ATOMIC_CONSTEXPR bool IsRequestPlanarReflection() const { return _flags & REQUEST_PLANAR_REFLECTION; } + WI_ATOMIC_CONSTEXPR bool IsLightmapRenderRequested() const { return _flags & LIGHTMAP_RENDER_REQUEST; } + WI_ATOMIC_CONSTEXPR bool IsLightmapDisableBlockCompression() const { return _flags & LIGHTMAP_DISABLE_BLOCK_COMPRESSION; } + WI_ATOMIC_CONSTEXPR bool IsForeground() const { return _flags & FOREGROUND; } + WI_ATOMIC_CONSTEXPR bool IsNotVisibleInMainCamera() const { return _flags & NOT_VISIBLE_IN_MAIN_CAMERA; } + WI_ATOMIC_CONSTEXPR bool IsNotVisibleInReflections() const { return _flags & NOT_VISIBLE_IN_REFLECTIONS; } + WI_ATOMIC_CONSTEXPR bool IsWetmapEnabled() const { return _flags & WETMAP_ENABLED; } constexpr float GetTransparency() const { return 1 - color.w; } constexpr uint32_t GetFilterMask() const { return filterMask | filterMaskDynamic; } @@ -1255,7 +1255,7 @@ namespace wi::scene _DEPRECATED_FORCE_RESET = 1 << 2, WIND = 1 << 3, }; - uint32_t _flags = DISABLE_DEACTIVATION; + wi::copyable_atomic _flags = DISABLE_DEACTIVATION; float mass = 1.0f; float friction = 0.5f; @@ -1277,8 +1277,8 @@ namespace wi::scene constexpr void SetDisableDeactivation(bool value) { if (value) { _flags |= DISABLE_DEACTIVATION; } else { _flags &= ~DISABLE_DEACTIVATION; } } constexpr void SetWindEnabled(bool value) { if (value) { _flags |= WIND; } else { _flags &= ~WIND; } } - constexpr bool IsDisableDeactivation() const { return _flags & DISABLE_DEACTIVATION; } - constexpr bool IsWindEnabled() const { return _flags & WIND; } + WI_ATOMIC_CONSTEXPR bool IsDisableDeactivation() const { return _flags & DISABLE_DEACTIVATION; } + WI_ATOMIC_CONSTEXPR bool IsWindEnabled() const { return _flags & WIND; } void Reset() { @@ -1305,7 +1305,7 @@ namespace wi::scene { EMPTY = 0, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; uint32_t gpuBoneOffset = 0; // non-serialized, but better padding here wi::vector boneCollection; @@ -1329,7 +1329,7 @@ namespace wi::scene LIGHTMAPONLY_STATIC = 1 << 3, VOLUMETRICCLOUDS = 1 << 4, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; enum LightType : uint32_t { @@ -1374,12 +1374,12 @@ namespace wi::scene constexpr void SetStatic(bool value) { if (value) { _flags |= LIGHTMAPONLY_STATIC; } else { _flags &= ~LIGHTMAPONLY_STATIC; } } constexpr void SetVolumetricCloudsEnabled(bool value) { if (value) { _flags |= VOLUMETRICCLOUDS; } else { _flags &= ~VOLUMETRICCLOUDS; } } - constexpr bool IsCastingShadow() const { return _flags & CAST_SHADOW; } - constexpr bool IsVolumetricsEnabled() const { return _flags & VOLUMETRICS; } - constexpr bool IsVisualizerEnabled() const { return _flags & VISUALIZER; } - constexpr bool IsStatic() const { return _flags & LIGHTMAPONLY_STATIC; } - constexpr bool IsVolumetricCloudsEnabled() const { return _flags & VOLUMETRICCLOUDS; } - constexpr bool IsInactive() const { return intensity < 0.0001f || range < 0.0001f; } + WI_ATOMIC_CONSTEXPR bool IsCastingShadow() const { return _flags & CAST_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsVolumetricsEnabled() const { return _flags & VOLUMETRICS; } + WI_ATOMIC_CONSTEXPR bool IsVisualizerEnabled() const { return _flags & VISUALIZER; } + WI_ATOMIC_CONSTEXPR bool IsStatic() const { return _flags & LIGHTMAPONLY_STATIC; } + WI_ATOMIC_CONSTEXPR bool IsVolumetricCloudsEnabled() const { return _flags & VOLUMETRICCLOUDS; } + WI_ATOMIC_CONSTEXPR bool IsInactive() const { return intensity < 0.0001f || range < 0.0001f; } constexpr float GetRange() const { @@ -1421,7 +1421,7 @@ namespace wi::scene ORTHO = 1 << 2, CRT_FILTER = 1 << 3, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; float width = 0.0f; float height = 0.0f; @@ -1509,10 +1509,10 @@ namespace wi::scene constexpr void SetCustomProjectionEnabled(bool value = true) { if (value) { _flags |= CUSTOM_PROJECTION; } else { _flags &= ~CUSTOM_PROJECTION; } } constexpr void SetOrtho(bool value = true) { if (value) { _flags |= ORTHO; } else { _flags &= ~ORTHO; } SetDirty(); } constexpr void SetCRT(bool value = true) { if (value) { _flags |= CRT_FILTER; } else { _flags &= ~CRT_FILTER; } SetDirty(); } - constexpr bool IsDirty() const { return _flags & DIRTY; } - constexpr bool IsCustomProjectionEnabled() const { return _flags & CUSTOM_PROJECTION; } - constexpr bool IsOrtho() const { return _flags & ORTHO; } - constexpr bool IsCRT() const { return _flags & CRT_FILTER; } + WI_ATOMIC_CONSTEXPR bool IsDirty() const { return _flags & DIRTY; } + WI_ATOMIC_CONSTEXPR bool IsCustomProjectionEnabled() const { return _flags & CUSTOM_PROJECTION; } + WI_ATOMIC_CONSTEXPR bool IsOrtho() const { return _flags & ORTHO; } + WI_ATOMIC_CONSTEXPR bool IsCRT() const { return _flags & CRT_FILTER; } void Lerp(const CameraComponent& a, const CameraComponent& b, float t); @@ -1529,7 +1529,7 @@ namespace wi::scene REALTIME = 1 << 1, MSAA = 1 << 2, }; - uint32_t _flags = DIRTY; + wi::copyable_atomic _flags = DIRTY; uint32_t resolution = 128; // power of two float realtime_update_interval = 0.0f; // how often to render when realtime (in seconds, 0 = every frame) std::string textureName; // if texture is coming from an asset @@ -1570,7 +1570,7 @@ namespace wi::scene { EMPTY = 0, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; enum class Type { @@ -1597,14 +1597,14 @@ namespace wi::scene EMPTY = 0, BASECOLOR_ONLY_ALPHA = 1 << 0, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; float slopeBlendPower = 0; // Set decal to only use alpha from base color texture. Useful for blending normalmap-only decals constexpr void SetBaseColorOnlyAlpha(bool value) { if (value) { _flags |= BASECOLOR_ONLY_ALPHA; } else { _flags &= ~BASECOLOR_ONLY_ALPHA; } } - constexpr bool IsBaseColorOnlyAlpha() const { return _flags & BASECOLOR_ONLY_ALPHA; } + WI_ATOMIC_CONSTEXPR bool IsBaseColorOnlyAlpha() const { return _flags & BASECOLOR_ONLY_ALPHA; } // Non-serialized attributes: float emissive; @@ -1633,7 +1633,7 @@ namespace wi::scene { EMPTY = 0, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; wi::vector keyframe_times; wi::vector keyframe_data; @@ -1651,7 +1651,7 @@ namespace wi::scene ROOT_MOTION = 1 << 2, PING_PONG = 1 << 3, }; - uint32_t _flags = LOOPED; + wi::copyable_atomic _flags = LOOPED; float start = 0; float end = 0; float timer = 0; @@ -1664,7 +1664,7 @@ namespace wi::scene { EMPTY = 0, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; wi::ecs::Entity target = wi::ecs::INVALID_ENTITY; int samplerIndex = -1; @@ -1741,7 +1741,7 @@ namespace wi::scene { EMPTY = 0, }; - uint32_t _flags = LOOPED; + wi::copyable_atomic _flags = LOOPED; wi::ecs::Entity data = wi::ecs::INVALID_ENTITY; @@ -1786,23 +1786,23 @@ namespace wi::scene float prevRotTimer; // Root Motion - constexpr bool IsPlaying() const { return _flags & PLAYING; } - constexpr bool IsLooped() const { return _flags & LOOPED; } - constexpr bool IsPingPong() const { return _flags & PING_PONG; } - constexpr bool IsPlayingOnce() const { return (_flags & (LOOPED | PING_PONG)) == 0; } + WI_ATOMIC_CONSTEXPR bool IsPlaying() const { return _flags & PLAYING; } + WI_ATOMIC_CONSTEXPR bool IsLooped() const { return _flags & LOOPED; } + WI_ATOMIC_CONSTEXPR bool IsPingPong() const { return _flags & PING_PONG; } + WI_ATOMIC_CONSTEXPR bool IsPlayingOnce() const { return (_flags & (LOOPED | PING_PONG)) == 0; } constexpr float GetLength() const { return end - start; } - constexpr bool IsEnded() const { return timer >= end; } - constexpr bool IsRootMotion() const { return _flags & ROOT_MOTION; } + WI_ATOMIC_CONSTEXPR bool IsEnded() const { return timer >= end; } + WI_ATOMIC_CONSTEXPR bool IsRootMotion() const { return _flags & ROOT_MOTION; } - constexpr void Play() { _flags |= PLAYING; } - constexpr void Pause() { _flags &= ~PLAYING; } - constexpr void Stop() { Pause(); timer = 0.0f; last_update_time = timer; } - constexpr void SetLooped(bool value = true) { if (value) { _flags |= LOOPED; _flags &= ~PING_PONG; } else { _flags &= ~LOOPED; } } - constexpr void SetPingPong(bool value = true) { if (value) { _flags |= PING_PONG; _flags &= ~LOOPED; } else { _flags &= ~PING_PONG; } } - constexpr void SetPlayOnce() { _flags &= ~(LOOPED | PING_PONG); } + WI_ATOMIC_CONSTEXPR void Play() { _flags |= PLAYING; } + WI_ATOMIC_CONSTEXPR void Pause() { _flags &= ~PLAYING; } + WI_ATOMIC_CONSTEXPR void Stop() { Pause(); timer = 0.0f; last_update_time = timer; } + WI_ATOMIC_CONSTEXPR void SetLooped(bool value = true) { if (value) { _flags |= LOOPED; _flags &= ~PING_PONG; } else { _flags &= ~LOOPED; } } + WI_ATOMIC_CONSTEXPR void SetPingPong(bool value = true) { if (value) { _flags |= PING_PONG; _flags &= ~LOOPED; } else { _flags &= ~PING_PONG; } } + WI_ATOMIC_CONSTEXPR void SetPlayOnce() { _flags &= ~(LOOPED | PING_PONG); } - constexpr void RootMotionOn() { _flags |= ROOT_MOTION; } - constexpr void RootMotionOff() { _flags &= ~ROOT_MOTION; } + WI_ATOMIC_CONSTEXPR void RootMotionOn() { _flags |= ROOT_MOTION; } + WI_ATOMIC_CONSTEXPR void RootMotionOff() { _flags &= ~ROOT_MOTION; } constexpr XMFLOAT3 GetRootTranslation() const { return rootTranslationOffset; } constexpr XMFLOAT4 GetRootRotation() const { return rootRotationOffset; } constexpr wi::ecs::Entity GetRootMotionBone() const { return rootMotionBone; } @@ -1828,29 +1828,29 @@ namespace wi::scene REALISTIC_SKY_RECEIVE_SHADOW = 1 << 9, VOLUMETRIC_CLOUDS_RECEIVE_SHADOW = 1 << 10, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; - constexpr bool IsOceanEnabled() const { return _flags & OCEAN_ENABLED; } - constexpr bool IsRealisticSky() const { return _flags & REALISTIC_SKY; } - constexpr bool IsVolumetricClouds() const { return _flags & VOLUMETRIC_CLOUDS; } - constexpr bool IsHeightFog() const { return _flags & HEIGHT_FOG; } - constexpr bool IsVolumetricCloudsCastShadow() const { return _flags & VOLUMETRIC_CLOUDS_CAST_SHADOW; } - constexpr bool IsOverrideFogColor() const { return _flags & OVERRIDE_FOG_COLOR; } - constexpr bool IsRealisticSkyAerialPerspective() const { return _flags & REALISTIC_SKY_AERIAL_PERSPECTIVE; } - constexpr bool IsRealisticSkyHighQuality() const { return _flags & REALISTIC_SKY_HIGH_QUALITY; } - constexpr bool IsRealisticSkyReceiveShadow() const { return _flags & REALISTIC_SKY_RECEIVE_SHADOW; } - constexpr bool IsVolumetricCloudsReceiveShadow() const { return _flags & VOLUMETRIC_CLOUDS_RECEIVE_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsOceanEnabled() const { return _flags & OCEAN_ENABLED; } + WI_ATOMIC_CONSTEXPR bool IsRealisticSky() const { return _flags & REALISTIC_SKY; } + WI_ATOMIC_CONSTEXPR bool IsVolumetricClouds() const { return _flags & VOLUMETRIC_CLOUDS; } + WI_ATOMIC_CONSTEXPR bool IsHeightFog() const { return _flags & HEIGHT_FOG; } + WI_ATOMIC_CONSTEXPR bool IsVolumetricCloudsCastShadow() const { return _flags & VOLUMETRIC_CLOUDS_CAST_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsOverrideFogColor() const { return _flags & OVERRIDE_FOG_COLOR; } + WI_ATOMIC_CONSTEXPR bool IsRealisticSkyAerialPerspective() const { return _flags & REALISTIC_SKY_AERIAL_PERSPECTIVE; } + WI_ATOMIC_CONSTEXPR bool IsRealisticSkyHighQuality() const { return _flags & REALISTIC_SKY_HIGH_QUALITY; } + WI_ATOMIC_CONSTEXPR bool IsRealisticSkyReceiveShadow() const { return _flags & REALISTIC_SKY_RECEIVE_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsVolumetricCloudsReceiveShadow() const { return _flags & VOLUMETRIC_CLOUDS_RECEIVE_SHADOW; } - constexpr void SetOceanEnabled(bool value = true) { if (value) { _flags |= OCEAN_ENABLED; } else { _flags &= ~OCEAN_ENABLED; } } - constexpr void SetRealisticSky(bool value = true) { if (value) { _flags |= REALISTIC_SKY; } else { _flags &= ~REALISTIC_SKY; } } - constexpr void SetVolumetricClouds(bool value = true) { if (value) { _flags |= VOLUMETRIC_CLOUDS; } else { _flags &= ~VOLUMETRIC_CLOUDS; } } - constexpr void SetHeightFog(bool value = true) { if (value) { _flags |= HEIGHT_FOG; } else { _flags &= ~HEIGHT_FOG; } } - constexpr void SetVolumetricCloudsCastShadow(bool value = true) { if (value) { _flags |= VOLUMETRIC_CLOUDS_CAST_SHADOW; } else { _flags &= ~VOLUMETRIC_CLOUDS_CAST_SHADOW; } } - constexpr void SetOverrideFogColor(bool value = true) { if (value) { _flags |= OVERRIDE_FOG_COLOR; } else { _flags &= ~OVERRIDE_FOG_COLOR; } } - constexpr void SetRealisticSkyAerialPerspective(bool value = true) { if (value) { _flags |= REALISTIC_SKY_AERIAL_PERSPECTIVE; } else { _flags &= ~REALISTIC_SKY_AERIAL_PERSPECTIVE; } } - constexpr void SetRealisticSkyHighQuality(bool value = true) { if (value) { _flags |= REALISTIC_SKY_HIGH_QUALITY; } else { _flags &= ~REALISTIC_SKY_HIGH_QUALITY; } } - constexpr void SetRealisticSkyReceiveShadow(bool value = true) { if (value) { _flags |= REALISTIC_SKY_RECEIVE_SHADOW; } else { _flags &= ~REALISTIC_SKY_RECEIVE_SHADOW; } } - constexpr void SetVolumetricCloudsReceiveShadow(bool value = true) { if (value) { _flags |= VOLUMETRIC_CLOUDS_RECEIVE_SHADOW; } else { _flags &= ~VOLUMETRIC_CLOUDS_RECEIVE_SHADOW; } } + WI_ATOMIC_CONSTEXPR void SetOceanEnabled(bool value = true) { if (value) { _flags |= OCEAN_ENABLED; } else { _flags &= ~OCEAN_ENABLED; } } + WI_ATOMIC_CONSTEXPR void SetRealisticSky(bool value = true) { if (value) { _flags |= REALISTIC_SKY; } else { _flags &= ~REALISTIC_SKY; } } + WI_ATOMIC_CONSTEXPR void SetVolumetricClouds(bool value = true) { if (value) { _flags |= VOLUMETRIC_CLOUDS; } else { _flags &= ~VOLUMETRIC_CLOUDS; } } + WI_ATOMIC_CONSTEXPR void SetHeightFog(bool value = true) { if (value) { _flags |= HEIGHT_FOG; } else { _flags &= ~HEIGHT_FOG; } } + WI_ATOMIC_CONSTEXPR void SetVolumetricCloudsCastShadow(bool value = true) { if (value) { _flags |= VOLUMETRIC_CLOUDS_CAST_SHADOW; } else { _flags &= ~VOLUMETRIC_CLOUDS_CAST_SHADOW; } } + WI_ATOMIC_CONSTEXPR void SetOverrideFogColor(bool value = true) { if (value) { _flags |= OVERRIDE_FOG_COLOR; } else { _flags &= ~OVERRIDE_FOG_COLOR; } } + WI_ATOMIC_CONSTEXPR void SetRealisticSkyAerialPerspective(bool value = true) { if (value) { _flags |= REALISTIC_SKY_AERIAL_PERSPECTIVE; } else { _flags &= ~REALISTIC_SKY_AERIAL_PERSPECTIVE; } } + WI_ATOMIC_CONSTEXPR void SetRealisticSkyHighQuality(bool value = true) { if (value) { _flags |= REALISTIC_SKY_HIGH_QUALITY; } else { _flags &= ~REALISTIC_SKY_HIGH_QUALITY; } } + WI_ATOMIC_CONSTEXPR void SetRealisticSkyReceiveShadow(bool value = true) { if (value) { _flags |= REALISTIC_SKY_RECEIVE_SHADOW; } else { _flags &= ~REALISTIC_SKY_RECEIVE_SHADOW; } } + WI_ATOMIC_CONSTEXPR void SetVolumetricCloudsReceiveShadow(bool value = true) { if (value) { _flags |= VOLUMETRIC_CLOUDS_RECEIVE_SHADOW; } else { _flags &= ~VOLUMETRIC_CLOUDS_RECEIVE_SHADOW; } } XMFLOAT3 sunColor = XMFLOAT3(0, 0, 0); XMFLOAT3 sunDirection = XMFLOAT3(0, 1, 0); @@ -1905,16 +1905,16 @@ namespace wi::scene LOOPED = 1 << 1, DISABLE_3D = 1 << 2, }; - uint32_t _flags = LOOPED; + wi::copyable_atomic _flags = LOOPED; std::string filename; wi::Resource soundResource; wi::audio::SoundInstance soundinstance; float volume = 1; - constexpr bool IsPlaying() const { return _flags & PLAYING; } - constexpr bool IsLooped() const { return _flags & LOOPED; } - constexpr bool IsDisable3D() const { return _flags & DISABLE_3D; } + WI_ATOMIC_CONSTEXPR bool IsPlaying() const { return _flags & PLAYING; } + WI_ATOMIC_CONSTEXPR bool IsLooped() const { return _flags & LOOPED; } + WI_ATOMIC_CONSTEXPR bool IsDisable3D() const { return _flags & DISABLE_3D; } void Play(); void Stop(); @@ -1932,20 +1932,20 @@ namespace wi::scene PLAYING = 1 << 0, LOOPED = 1 << 1, }; - uint32_t _flags = LOOPED; + wi::copyable_atomic _flags = LOOPED; std::string filename; wi::Resource videoResource; wi::video::VideoInstance videoinstance; float currentTimer = 0; // The current playback timer is reflected in this - constexpr bool IsPlaying() const { return _flags & PLAYING; } - constexpr bool IsLooped() const { return _flags & LOOPED; } + WI_ATOMIC_CONSTEXPR bool IsPlaying() const { return _flags & PLAYING; } + WI_ATOMIC_CONSTEXPR bool IsLooped() const { return _flags & LOOPED; } - constexpr void Play() { _flags |= PLAYING; } - constexpr void Pause() { _flags &= ~PLAYING; } + WI_ATOMIC_CONSTEXPR void Play() { _flags |= PLAYING; } + WI_ATOMIC_CONSTEXPR void Pause() { _flags &= ~PLAYING; } void Stop() { Pause(); Seek(0); } - constexpr void SetLooped(bool value = true) { if (value) { _flags |= LOOPED; } else { _flags &= ~LOOPED; } } + WI_ATOMIC_CONSTEXPR void SetLooped(bool value = true) { if (value) { _flags |= LOOPED; } else { _flags &= ~LOOPED; } } // Get total length of video in seconds float GetLength() const; @@ -1963,7 +1963,7 @@ namespace wi::scene EMPTY = 0, DISABLED = 1 << 0, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; wi::ecs::Entity target = wi::ecs::INVALID_ENTITY; // which entity to follow (must have a transform component) uint32_t chain_length = 0; // recursive depth @@ -1973,8 +1973,8 @@ namespace wi::scene bool use_target_position = false; XMFLOAT3 target_position = XMFLOAT3(0, 0, 0); - constexpr void SetDisabled(bool value = true) { if (value) { _flags |= DISABLED; } else { _flags &= ~DISABLED; } } - constexpr bool IsDisabled() const { return _flags & DISABLED; } + WI_ATOMIC_CONSTEXPR void SetDisabled(bool value = true) { if (value) { _flags |= DISABLED; } else { _flags &= ~DISABLED; } } + WI_ATOMIC_CONSTEXPR bool IsDisabled() const { return _flags & DISABLED; } void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; @@ -1989,7 +1989,7 @@ namespace wi::scene _DEPRECATED_STRETCH_ENABLED = 1 << 2, GRAVITY_ENABLED = 1 << 3, }; - uint32_t _flags = RESET | GRAVITY_ENABLED; + wi::copyable_atomic _flags = RESET | GRAVITY_ENABLED; float stiffnessForce = 0.5f; float dragForce = 0.5f; @@ -2010,13 +2010,13 @@ namespace wi::scene TransformComponent* transform = nullptr; TransformComponent* parent_transform = nullptr; - constexpr void Reset(bool value = true) { if (value) { _flags |= RESET; } else { _flags &= ~RESET; } } - constexpr void SetDisabled(bool value = true) { if (value) { _flags |= DISABLED; } else { _flags &= ~DISABLED; } } - constexpr void SetGravityEnabled(bool value) { if (value) { _flags |= GRAVITY_ENABLED; } else { _flags &= ~GRAVITY_ENABLED; } } + WI_ATOMIC_CONSTEXPR void Reset(bool value = true) { if (value) { _flags |= RESET; } else { _flags &= ~RESET; } } + WI_ATOMIC_CONSTEXPR void SetDisabled(bool value = true) { if (value) { _flags |= DISABLED; } else { _flags &= ~DISABLED; } } + WI_ATOMIC_CONSTEXPR void SetGravityEnabled(bool value) { if (value) { _flags |= GRAVITY_ENABLED; } else { _flags &= ~GRAVITY_ENABLED; } } - constexpr bool IsResetting() const { return _flags & RESET; } - constexpr bool IsDisabled() const { return _flags & DISABLED; } - constexpr bool IsGravityEnabled() const { return _flags & GRAVITY_ENABLED; } + WI_ATOMIC_CONSTEXPR bool IsResetting() const { return _flags & RESET; } + WI_ATOMIC_CONSTEXPR bool IsDisabled() const { return _flags & DISABLED; } + WI_ATOMIC_CONSTEXPR bool IsGravityEnabled() const { return _flags & GRAVITY_ENABLED; } void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; @@ -2030,15 +2030,15 @@ namespace wi::scene GPU = 1 << 1, CAPSULE_SHADOW = 1 << 2, }; - uint32_t _flags = CPU; + wi::copyable_atomic _flags = CPU; - constexpr void SetCPUEnabled(bool value = true) { if (value) { _flags |= CPU; } else { _flags &= ~CPU; } } - constexpr void SetGPUEnabled(bool value = true) { if (value) { _flags |= GPU; } else { _flags &= ~GPU; } } - constexpr void SetCapsuleShadowEnabled(bool value = true) { if (value) { _flags |= CAPSULE_SHADOW; } else { _flags &= ~CAPSULE_SHADOW; } } + WI_ATOMIC_CONSTEXPR void SetCPUEnabled(bool value = true) { if (value) { _flags |= CPU; } else { _flags &= ~CPU; } } + WI_ATOMIC_CONSTEXPR void SetGPUEnabled(bool value = true) { if (value) { _flags |= GPU; } else { _flags &= ~GPU; } } + WI_ATOMIC_CONSTEXPR void SetCapsuleShadowEnabled(bool value = true) { if (value) { _flags |= CAPSULE_SHADOW; } else { _flags &= ~CAPSULE_SHADOW; } } - constexpr bool IsCPUEnabled() const { return _flags & CPU; } - constexpr bool IsGPUEnabled() const { return _flags & GPU; } - constexpr bool IsCapsuleShadowEnabled() const { return _flags & CAPSULE_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsCPUEnabled() const { return _flags & CPU; } + WI_ATOMIC_CONSTEXPR bool IsGPUEnabled() const { return _flags & GPU; } + WI_ATOMIC_CONSTEXPR bool IsCapsuleShadowEnabled() const { return _flags & CAPSULE_SHADOW; } enum class Shape { @@ -2077,18 +2077,18 @@ namespace wi::scene PLAYING = 1 << 0, PLAY_ONCE = 1 << 1, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; // Non-serialized attributes: wi::Resource resource; size_t script_hash = 0; - constexpr void Play() { _flags |= PLAYING; } - constexpr void SetPlayOnce(bool once = true) { if (once) { _flags |= PLAY_ONCE; } else { _flags &= ~PLAY_ONCE; } } - constexpr void Stop() { _flags &= ~PLAYING; } + WI_ATOMIC_CONSTEXPR void Play() { _flags |= PLAYING; } + WI_ATOMIC_CONSTEXPR void SetPlayOnce(bool once = true) { if (once) { _flags |= PLAY_ONCE; } else { _flags &= ~PLAY_ONCE; } } + WI_ATOMIC_CONSTEXPR void Stop() { _flags &= ~PLAYING; } - constexpr bool IsPlaying() const { return _flags & PLAYING; } - constexpr bool IsPlayingOnlyOnce() const { return _flags & PLAY_ONCE; } + WI_ATOMIC_CONSTEXPR bool IsPlaying() const { return _flags & PLAYING; } + WI_ATOMIC_CONSTEXPR bool IsPlayingOnlyOnce() const { return _flags & PLAY_ONCE; } void CreateFromFile(const std::string& filename); @@ -2103,7 +2103,7 @@ namespace wi::scene FORCE_TALKING = 1 << 0, TALKING_ENDED = 1 << 1, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; // Preset expressions can have common behaviours assigned: // https://github.com/vrm-c/vrm-specification/blob/bd205a6c3839993f2729e4e7c3a74af89877cfce/specification/VRMC_vrm-1.0-beta/expressions.md#preset-expressions @@ -2168,7 +2168,7 @@ namespace wi::scene DIRTY = 1 << 0, BINARY = 1 << 1, }; - uint32_t _flags = EMPTY; + wi::copyable_atomic _flags = EMPTY; std::string name; float weight = 0; @@ -2186,11 +2186,11 @@ namespace wi::scene }; wi::vector morph_target_bindings; - constexpr bool IsDirty() const { return _flags & DIRTY; } - constexpr bool IsBinary() const { return _flags & BINARY; } + WI_ATOMIC_CONSTEXPR bool IsDirty() const { return _flags & DIRTY; } + WI_ATOMIC_CONSTEXPR bool IsBinary() const { return _flags & BINARY; } - constexpr void SetDirty(bool value = true) { if (value) { _flags |= DIRTY; } else { _flags &= ~DIRTY; } } - constexpr void SetBinary(bool value = true) { if (value) { _flags |= BINARY; } else { _flags &= ~BINARY; } } + WI_ATOMIC_CONSTEXPR void SetDirty(bool value = true) { if (value) { _flags |= DIRTY; } else { _flags &= ~DIRTY; } } + WI_ATOMIC_CONSTEXPR void SetBinary(bool value = true) { if (value) { _flags |= BINARY; } else { _flags &= ~BINARY; } } // Set weight of expression (also sets dirty state if value is out of date) inline void SetWeight(float value) @@ -2204,10 +2204,10 @@ namespace wi::scene }; wi::vector expressions; - constexpr bool IsForceTalkingEnabled() const { return _flags & FORCE_TALKING; } + WI_ATOMIC_CONSTEXPR bool IsForceTalkingEnabled() const { return _flags & FORCE_TALKING; } // Force continuous talking animation, even if no voice is playing - constexpr void SetForceTalkingEnabled(bool value = true) { if (value) { _flags |= FORCE_TALKING; } else { _flags &= ~FORCE_TALKING; _flags |= TALKING_ENDED; } } + WI_ATOMIC_CONSTEXPR void SetForceTalkingEnabled(bool value = true) { if (value) { _flags |= FORCE_TALKING; } else { _flags &= ~FORCE_TALKING; _flags |= TALKING_ENDED; } } // Non-serialized attributes: float blink_timer = 0; @@ -2230,7 +2230,7 @@ namespace wi::scene DISABLE_INTERSECTION = 1 << 2, DISABLE_CAPSULE_SHADOW = 1 << 3, }; - uint32_t _flags = LOOKAT; + wi::copyable_atomic _flags = LOOKAT; // https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_vrm-1.0-beta/humanoid.md#list-of-humanoid-bones enum class HumanoidBone @@ -2304,15 +2304,15 @@ namespace wi::scene }; wi::ecs::Entity bones[size_t(HumanoidBone::Count)] = {}; - constexpr bool IsLookAtEnabled() const { return _flags & LOOKAT; } - constexpr bool IsRagdollPhysicsEnabled() const { return _flags & RAGDOLL_PHYSICS; } - constexpr bool IsIntersectionDisabled() const { return _flags & DISABLE_INTERSECTION; } - constexpr bool IsCapsuleShadowDisabled() const { return _flags & DISABLE_CAPSULE_SHADOW; } + WI_ATOMIC_CONSTEXPR bool IsLookAtEnabled() const { return _flags & LOOKAT; } + WI_ATOMIC_CONSTEXPR bool IsRagdollPhysicsEnabled() const { return _flags & RAGDOLL_PHYSICS; } + WI_ATOMIC_CONSTEXPR bool IsIntersectionDisabled() const { return _flags & DISABLE_INTERSECTION; } + WI_ATOMIC_CONSTEXPR bool IsCapsuleShadowDisabled() const { return _flags & DISABLE_CAPSULE_SHADOW; } - constexpr void SetLookAtEnabled(bool value = true) { if (value) { _flags |= LOOKAT; } else { _flags &= ~LOOKAT; } } - constexpr void SetRagdollPhysicsEnabled(bool value = true) { if (value) { _flags |= RAGDOLL_PHYSICS; } else { _flags &= ~RAGDOLL_PHYSICS; } } - constexpr void SetIntersectionDisabled(bool value = true) { if (value) { _flags |= DISABLE_INTERSECTION; } else { _flags &= ~DISABLE_INTERSECTION; } } - constexpr void SetCapsuleShadowDisabled(bool value = true) { if (value) { _flags |= DISABLE_CAPSULE_SHADOW; } else { _flags &= ~DISABLE_CAPSULE_SHADOW; } } + WI_ATOMIC_CONSTEXPR void SetLookAtEnabled(bool value = true) { if (value) { _flags |= LOOKAT; } else { _flags &= ~LOOKAT; } } + WI_ATOMIC_CONSTEXPR void SetRagdollPhysicsEnabled(bool value = true) { if (value) { _flags |= RAGDOLL_PHYSICS; } else { _flags &= ~RAGDOLL_PHYSICS; } } + WI_ATOMIC_CONSTEXPR void SetIntersectionDisabled(bool value = true) { if (value) { _flags |= DISABLE_INTERSECTION; } else { _flags &= ~DISABLE_INTERSECTION; } } + WI_ATOMIC_CONSTEXPR void SetCapsuleShadowDisabled(bool value = true) { if (value) { _flags |= DISABLE_CAPSULE_SHADOW; } else { _flags &= ~DISABLE_CAPSULE_SHADOW; } } XMFLOAT2 head_rotation_max = XMFLOAT2(XM_PI / 3.0f, XM_PI / 6.0f); XMFLOAT2 eye_rotation_max = XMFLOAT2(XM_PI / 20.0f, XM_PI / 20.0f); @@ -2358,7 +2358,7 @@ namespace wi::scene { NONE = 0, }; - uint32_t _flags = NONE; + wi::copyable_atomic _flags = NONE; enum class Preset { @@ -2462,7 +2462,7 @@ namespace wi::scene DEDICATED_SHADOW = 1 << 1, ACTIVE = 1 << 2, }; - uint32_t _flags = ACTIVE; + wi::copyable_atomic _flags = ACTIVE; int health = 100; float width = 0.4f; // capsule radius @@ -2626,7 +2626,7 @@ namespace wi::scene LOOPED = 1 << 1, DIRTY = 1 << 2, }; - uint32_t _flags = NONE; + wi::copyable_atomic _flags = NONE; float width = 1; // overall width multiplier for all nodes (affects mesh generation) float rotation = 0; // rotation of nodes in radians around the spline axis (affects mesh generation)