Skip to content

Commit 3243f17

Browse files
committed
wip
1 parent 33cffdd commit 3243f17

8 files changed

Lines changed: 304 additions & 191 deletions

File tree

WickedEngine/wiArchive.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "wiVector.h"
55
#include "wiColor.h"
66
#include "wiGraphics.h"
7+
#include "wiAtomic.h"
78

89
#include <string>
910

@@ -469,6 +470,22 @@ namespace wi
469470
}
470471
return *this;
471472
}
473+
template<typename T>
474+
inline Archive& operator>>(std::atomic<T>& data)
475+
{
476+
T val;
477+
(*this) >> val;
478+
data = val;
479+
return *this;
480+
}
481+
template<typename T>
482+
inline Archive& operator>>(wi::relaxed_atomic<T>& data)
483+
{
484+
T val;
485+
(*this) >> val;
486+
data = val;
487+
return *this;
488+
}
472489

473490

474491

WickedEngine/wiAtomic.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#pragma once
2+
3+
#include <atomic>
4+
5+
// MSVC does not have constexpr atomic load(), so for now
6+
// we just test for that, in C++20 we could also use
7+
// type traits to check
8+
#if defined(_MSC_VER) && !defined(__clang__)
9+
#define WI_ATOMIC_CONSTEXPR inline
10+
#else
11+
#define WI_ATOMIC_CONSTEXPR constexpr
12+
#endif
13+
14+
namespace wi {
15+
16+
17+
// subclass of std::atomic that allows copying
18+
// note that copying *is not* atomic; this is a helper class that allows us
19+
// to copy classes/structs that contain atomic members where we can accept
20+
// that copying is not atomic
21+
template<typename T>
22+
struct copyable_atomic : public std::atomic<T> {
23+
copyable_atomic() noexcept = default;
24+
constexpr copyable_atomic(T v) noexcept : std::atomic<T>(v) {};
25+
copyable_atomic(const copyable_atomic& other) {
26+
std::atomic<T>::store(other.load());
27+
}
28+
copyable_atomic& operator=(const copyable_atomic& other) {
29+
std::atomic<T>::store(other.load());
30+
return *this;
31+
}
32+
};
33+
34+
// simple wrapper that always defaults to memory_order_relaxed
35+
template<typename T>
36+
struct relaxed_atomic {
37+
std::atomic<T> val;
38+
39+
relaxed_atomic() noexcept = default;
40+
constexpr relaxed_atomic(T v) noexcept : val(v) {};
41+
relaxed_atomic(const relaxed_atomic& other) : val(other.load(std::memory_order_relaxed)) {};
42+
relaxed_atomic& operator=(const relaxed_atomic& other) {
43+
val.store(other.val.load(std::memory_order_relaxed), std::memory_order_relaxed);
44+
return *this;
45+
}
46+
void store(T desired, std::memory_order order = std::memory_order_relaxed) {
47+
return val.store(desired, order);
48+
}
49+
T load(std::memory_order order = std::memory_order_relaxed) const {
50+
return val.load(order);
51+
}
52+
53+
T fetch_xor(T arg, std::memory_order order = std::memory_order_relaxed) {
54+
return val.fetch_xor(arg, order);
55+
}
56+
T fetch_and(T arg, std::memory_order order = std::memory_order_relaxed) {
57+
return val.fetch_add(arg, order);
58+
}
59+
T fetch_or(T arg, std::memory_order order = std::memory_order_relaxed) {
60+
return val.fetch_or(arg, order);
61+
}
62+
63+
operator T() const noexcept { return load(); }
64+
T operator=(T desired) noexcept { store(desired); return desired;}
65+
T operator&=(T arg) { return fetch_and(arg); }
66+
T operator|=(T arg) { return fetch_or(arg); }
67+
T operator^=(T arg) { return fetch_xor(arg); }
68+
};
69+
70+
71+
}

WickedEngine/wiBacklog.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ namespace wi::backlog
3636
wi::font::Params font_params;
3737
wi::Color backgroundColor = wi::Color(17, 30, 43, 255);
3838
Texture backgroundTex;
39-
bool refitscroll = false;
39+
std::atomic<bool> refitscroll = false;
4040
wi::gui::TextInputField inputField;
4141
wi::gui::Button toggleButton;
4242
wi::gui::GUI GUI;
4343

4444
bool locked = false;
4545
bool blockLuaExec = false;
4646
LogLevel logLevel = LogLevel::Default;
47-
LogLevel unseen = LogLevel::None;
47+
std::atomic<LogLevel> unseen = LogLevel::None;
4848

4949
std::deque<LogEntry> history;
5050
std::mutex historyLock;
@@ -494,15 +494,14 @@ namespace wi::backlog
494494
wi::font::SetCanvas(canvas); // always set here as it can be called from outside...
495495
wi::font::Params params = font_params;
496496
params.cursor = {};
497-
if (refitscroll)
497+
if (refitscroll.exchange(false, std::memory_order_relaxed))
498498
{
499499
const float textheight = wi::font::TextHeight(getText(), params);
500500
const float limit = canvas.GetLogicalHeight() - 50;
501501
if (scroll + textheight > limit)
502502
{
503503
scroll = limit - textheight;
504504
}
505-
refitscroll = false;
506505
}
507506
params.posX = 5;
508507
params.posY = pos + scroll;
@@ -599,7 +598,7 @@ namespace wi::backlog
599598
// Enqueue for async file writing
600599
asyncWriter.Enqueue(str);
601600

602-
refitscroll = true;
601+
refitscroll.store(true);
603602

604603
switch (level)
605604
{
@@ -615,7 +614,11 @@ namespace wi::backlog
615614
break;
616615
}
617616

618-
unseen = std::max(unseen, level);
617+
// atomic version of unseen = max(unseen, level)
618+
LogLevel current_unseen = unseen.load(std::memory_order_relaxed);
619+
while (current_unseen < level) {
620+
unseen.compare_exchange_weak(current_unseen, level, std::memory_order_acq_rel, std::memory_order_relaxed);
621+
}
619622

620623
// Force an immediate flush on errors to prevent potential data loss
621624
// in case the application is about to crash

WickedEngine/wiECS.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <cassert>
1313
#include <atomic>
1414
#include <memory>
15+
#include <shared_mutex>
1516
#include <string>
1617

1718
// Entity-Component System
@@ -604,14 +605,17 @@ namespace wi::ecs
604605
};
605606
Item items[64];
606607
};
608+
mutable std::shared_mutex mutex;
607609
wi::unordered_map<uint64_t, Block> table;
608610

609611
inline void clear()
610612
{
613+
std::unique_lock lock(mutex);
611614
table.clear();
612615
}
613616
inline void erase(Entity entity)
614617
{
618+
std::unique_lock lock(mutex);
615619
const uint64_t block_index = entity >> 6ull; // entity / 64
616620
auto it = table.find(block_index);
617621
if (it == table.end())
@@ -631,6 +635,7 @@ namespace wi::ecs
631635
}
632636
inline void insert(Entity entity, size_t index)
633637
{
638+
std::unique_lock lock(mutex);
634639
const uint64_t block_index = entity >> 6ull; // entity / 64
635640
const uint64_t item_index = entity & 63ull; // entity % 64
636641
Block& block = table[block_index];
@@ -639,6 +644,7 @@ namespace wi::ecs
639644
}
640645
inline size_t get(Entity entity) const
641646
{
647+
std::shared_lock lock(mutex);
642648
const uint64_t block_index = entity >> 6ull; // entity / 64
643649
const auto it = table.find(block_index);
644650
if (it == table.end())
@@ -653,21 +659,25 @@ namespace wi::ecs
653659
// Implementation with hash table:
654660
// The standard hashing method, performance depends on hashing, hash collisions
655661
wi::unordered_map<Entity, size_t> table;
656-
662+
mutable std::shared_mutex mutex;
657663
inline void clear()
658664
{
665+
std::unique_lock lock(mutex);
659666
table.clear();
660667
}
661668
inline void erase(Entity entity)
662669
{
670+
std::unique_lock lock(mutex);
663671
table.erase(entity);
664672
}
665673
inline void insert(Entity entity, size_t index)
666674
{
675+
std::unique_lock lock(mutex);
667676
table[entity] = index;
668677
}
669678
inline size_t get(Entity entity) const
670679
{
680+
std::shared_lock lock(mutex);
671681
if (table.empty())
672682
return INVALID_INDEX;
673683
auto it = table.find(entity);
@@ -694,13 +704,15 @@ namespace wi::ecs
694704
uint64_t version = 0;
695705
};
696706
wi::unordered_map<std::string, LibraryEntry> entries;
707+
mutable std::shared_mutex mutex;
697708

698709
// Create an instance of ComponentManager of a certain data type
699710
// The name must be unique, it will be used in serialization
700711
// version is optional, it will be propagated to ComponentManager::Serialize() inside the EntitySerializer parameter
701712
template<typename T>
702713
inline ComponentManager<T>& Register(const std::string& name, uint64_t version = 0)
703714
{
715+
std::unique_lock lock(mutex);
704716
entries[name].component_manager = std::make_unique<ComponentManager<T>>();
705717
entries[name].version = version;
706718
return static_cast<ComponentManager<T>&>(*entries[name].component_manager);
@@ -709,6 +721,7 @@ namespace wi::ecs
709721
template<typename T>
710722
inline ComponentManager<T>* Get(const std::string& name)
711723
{
724+
std::shared_lock lock(mutex);
712725
auto it = entries.find(name);
713726
if (it == entries.end())
714727
return nullptr;
@@ -718,6 +731,7 @@ namespace wi::ecs
718731
template<typename T>
719732
inline const ComponentManager<T>* Get(const std::string& name) const
720733
{
734+
std::shared_lock lock(mutex);
721735
auto it = entries.find(name);
722736
if (it == entries.end())
723737
return nullptr;
@@ -726,6 +740,7 @@ namespace wi::ecs
726740

727741
inline uint64_t GetVersion(std::string name) const
728742
{
743+
std::shared_lock lock(mutex);
729744
auto it = entries.find(name);
730745
if (it == entries.end())
731746
return 0;

WickedEngine/wiGraphicsDevice.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "wiGraphics.h"
44
#include "wiPlatform.h"
55

6+
#include <atomic>
67
#include <cassert>
78
#include <cstring>
89
#include <algorithm>
@@ -52,7 +53,7 @@ namespace wi::graphics
5253
{
5354
protected:
5455
static constexpr uint32_t BUFFERCOUNT = 2;
55-
uint64_t FRAMECOUNT = 0;
56+
std::atomic<uint64_t> FRAMECOUNT = 0;
5657
size_t SHADER_IDENTIFIER_SIZE = 0;
5758
size_t TOPLEVEL_ACCELERATION_STRUCTURE_INSTANCE_SIZE = 0;
5859
uint64_t TIMESTAMP_FREQUENCY = 0;
@@ -116,17 +117,23 @@ namespace wi::graphics
116117
// One PipelineState object can be compiled internally for multiple render target or depth-stencil formats, or sample counts
117118
virtual size_t GetActivePipelineCount() const = 0;
118119

120+
121+
#if defined(_MSC_VER) && !defined(__clang__)
122+
#define constexpr_no_msvc
123+
#else
124+
#define constexpr_no_msvc constexpr
125+
#endif
119126
// Returns the number of elapsed frames (submits)
120127
// It is incremented when calling SubmitCommandLists()
121-
constexpr uint64_t GetFrameCount() const { return FRAMECOUNT; }
128+
constexpr_no_msvc uint64_t GetFrameCount() const { return FRAMECOUNT; }
122129

123130
// Check whether the graphics device supports a feature or not
124131
constexpr bool CheckCapability(GraphicsDeviceCapability capability) const { return has_flag(capabilities, capability); }
125132

126133
// Returns the buffer count, which is the array size of buffered resources used by both the CPU and GPU
127134
static constexpr uint32_t GetBufferCount() { return BUFFERCOUNT; }
128135
// Returns the current buffer index, which is in range [0, GetBufferCount() - 1]
129-
constexpr uint32_t GetBufferIndex() const { return GetFrameCount() % GetBufferCount(); }
136+
constexpr_no_msvc uint32_t GetBufferIndex() const { return GetFrameCount() % GetBufferCount(); }
130137

131138
// Returns whether the graphics debug layer is enabled. It can be enabled when creating the device.
132139
constexpr bool IsDebugDevice() const { return validationMode != ValidationMode::Disabled; }
@@ -294,7 +301,7 @@ namespace wi::graphics
294301
inline bool IsValid() const { return data != nullptr && buffer.IsValid(); }
295302
};
296303

297-
// Allocates temporary memory that the CPU can write and GPU can read.
304+
// Allocates temporary memory that the CPU can write and GPU can read.
298305
// It is only alive for one frame and automatically invalidated after that.
299306
GPUAllocation AllocateGPU(uint64_t dataSize, CommandList cmd)
300307
{

WickedEngine/wiGraphicsDevice_DX12.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5388,7 +5388,7 @@ std::mutex queue_locker;
53885388
else
53895389
{
53905390
allocationhandler->destroylocker.lock();
5391-
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT));
5391+
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, GetFrameCount()));
53925392
allocationhandler->destroylocker.unlock();
53935393
}
53945394
}
@@ -5767,15 +5767,15 @@ std::mutex queue_locker;
57675767

57685768
for (auto& x : pipelines_global)
57695769
{
5770-
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT));
5770+
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, GetFrameCount()));
57715771
}
57725772
pipelines_global.clear();
57735773

57745774
for (auto& x : commandlists)
57755775
{
57765776
for (auto& y : x->pipelines_worker)
57775777
{
5778-
allocationhandler->destroyer_pipelines.push_back(std::make_pair(y.second, FRAMECOUNT));
5778+
allocationhandler->destroyer_pipelines.push_back(std::make_pair(y.second, GetFrameCount()));
57795779
}
57805780
x->pipelines_worker.clear();
57815781
}

WickedEngine/wiGraphicsDevice_Vulkan.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,7 @@ using namespace vulkan_internal;
16351635
if (descriptorPool != VK_NULL_HANDLE)
16361636
{
16371637
device->allocationhandler->destroylocker.lock();
1638-
device->allocationhandler->destroyer_descriptorPools.push_back(std::make_pair(descriptorPool, device->FRAMECOUNT));
1638+
device->allocationhandler->destroyer_descriptorPools.push_back(std::make_pair(descriptorPool, device->GetFrameCount()));
16391639
descriptorPool = VK_NULL_HANDLE;
16401640
device->allocationhandler->destroylocker.unlock();
16411641
}
@@ -7412,7 +7412,7 @@ using namespace vulkan_internal;
74127412
else
74137413
{
74147414
allocationhandler->destroylocker.lock();
7415-
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT));
7415+
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, GetFrameCount()));
74167416
allocationhandler->destroylocker.unlock();
74177417
}
74187418
}
@@ -7510,15 +7510,15 @@ using namespace vulkan_internal;
75107510

75117511
for (auto& x : pipelines_global)
75127512
{
7513-
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, FRAMECOUNT));
7513+
allocationhandler->destroyer_pipelines.push_back(std::make_pair(x.second, GetFrameCount()));
75147514
}
75157515
pipelines_global.clear();
75167516

75177517
for (auto& x : commandlists)
75187518
{
75197519
for (auto& y : x->pipelines_worker)
75207520
{
7521-
allocationhandler->destroyer_pipelines.push_back(std::make_pair(y.second, FRAMECOUNT));
7521+
allocationhandler->destroyer_pipelines.push_back(std::make_pair(y.second, GetFrameCount()));
75227522
}
75237523
x->pipelines_worker.clear();
75247524
}

0 commit comments

Comments
 (0)