升级
This commit is contained in:
+100
@@ -0,0 +1,100 @@
|
||||
param(
|
||||
[string]$DatovizBuildDirectory,
|
||||
[string]$CTestExecutable = 'ctest',
|
||||
[ValidateSet('Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel')]
|
||||
[string]$BuildType = 'Debug',
|
||||
[string]$Toolchain = 'vs2022'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$render3DRoot = $PSScriptRoot
|
||||
$workspaceRoot = Split-Path -Parent $render3DRoot
|
||||
$aeRoot = Split-Path -Parent (Split-Path -Parent $workspaceRoot)
|
||||
$cacheVariant = "${Toolchain}_${BuildType}"
|
||||
$pthreadsRuntimeDirectory = Join-Path $aeRoot "cached_external_library\${cacheVariant}_render_3D\install\PThreads4W\x86_64\${BuildType}\bin"
|
||||
$oneTBBRuntimeDirectory = Join-Path $aeRoot "cached_external_library\${cacheVariant}_render_kernel\install\oneTBB\bin"
|
||||
$windowsSdkRuntimeDirectory = 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64'
|
||||
$runtimeDirectories = @(
|
||||
$pthreadsRuntimeDirectory
|
||||
$oneTBBRuntimeDirectory
|
||||
$windowsSdkRuntimeDirectory
|
||||
)
|
||||
|
||||
$vulkanSdkRoot = if ($env:VULKAN_SDK) {
|
||||
$env:VULKAN_SDK
|
||||
} else {
|
||||
'C:\VulkanSDK\1.4.357.0'
|
||||
}
|
||||
$runtimeDirectories += Join-Path $vulkanSdkRoot 'Bin'
|
||||
$pthreadRuntimeName = if ($BuildType -eq 'Debug') { 'pthreadVC3d.dll' } else { 'pthreadVC3.dll' }
|
||||
$oneTBBRuntimeName = if ($BuildType -eq 'Debug') { 'tbb12_debug.dll' } else { 'tbb12.dll' }
|
||||
$shadercRuntimeLibrary = Join-Path $vulkanSdkRoot 'Bin\shaderc_shared.dll'
|
||||
$runtimeLibraries = @(
|
||||
(Join-Path $pthreadsRuntimeDirectory $pthreadRuntimeName)
|
||||
(Join-Path $oneTBBRuntimeDirectory $oneTBBRuntimeName)
|
||||
$shadercRuntimeLibrary
|
||||
)
|
||||
$linkDirectories = @(
|
||||
'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.44.35207\lib\x64'
|
||||
'C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\ucrt\x64'
|
||||
'C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\um\x64'
|
||||
)
|
||||
|
||||
$missingDirectories = @($runtimeDirectories | Where-Object { -not (Test-Path -LiteralPath $_ -PathType Container) })
|
||||
if ($missingDirectories.Count -ne 0) {
|
||||
throw "Renderive 3D runtime directories are missing: $($missingDirectories -join ', ')"
|
||||
}
|
||||
$missingLibraries = @($runtimeLibraries | Where-Object { -not (Test-Path -LiteralPath $_ -PathType Leaf) })
|
||||
if ($missingLibraries.Count -ne 0) {
|
||||
throw "Renderive 3D runtime libraries are missing: $($missingLibraries -join ', ')"
|
||||
}
|
||||
$missingLinkDirectories = @($linkDirectories | Where-Object { -not (Test-Path -LiteralPath $_ -PathType Container) })
|
||||
if ($missingLinkDirectories.Count -ne 0) {
|
||||
throw "Renderive 3D link directories are missing: $($missingLinkDirectories -join ', ')"
|
||||
}
|
||||
|
||||
$pathEntries = @($env:PATH -split ';' | Where-Object { $_ })
|
||||
foreach ($runtimeDirectory in $runtimeDirectories) {
|
||||
if ($pathEntries -notcontains $runtimeDirectory) {
|
||||
$pathEntries = @($runtimeDirectory) + $pathEntries
|
||||
}
|
||||
}
|
||||
$env:PATH = $pathEntries -join ';'
|
||||
|
||||
$libEntries = @($env:LIB -split ';' | Where-Object { $_ })
|
||||
foreach ($linkDirectory in $linkDirectories) {
|
||||
if ($libEntries -notcontains $linkDirectory) {
|
||||
$libEntries = @($linkDirectory) + $libEntries
|
||||
}
|
||||
}
|
||||
$env:LIB = $libEntries -join ';'
|
||||
|
||||
$env:VULKAN_SDK = $vulkanSdkRoot
|
||||
$env:DVZ_SHADERC_RUNTIME_LIBRARY = $shadercRuntimeLibrary
|
||||
$env:DVZ_WHEEL_RUNTIME_DIRS = Join-Path $vulkanSdkRoot 'Bin'
|
||||
|
||||
# Datoviz's upstream tests use POSIX-style absolute /tmp paths. On Windows those paths resolve
|
||||
# against the current drive root, so prepare the equivalent directory on the workspace drive.
|
||||
$windowsTempDirectory = Join-Path ([System.IO.Path]::GetPathRoot($workspaceRoot)) 'tmp'
|
||||
New-Item -ItemType Directory -Path $windowsTempDirectory -Force | Out-Null
|
||||
$env:TMPDIR = $windowsTempDirectory
|
||||
$env:TEMP = $windowsTempDirectory
|
||||
$env:TMP = $windowsTempDirectory
|
||||
|
||||
Write-Host 'Renderive render_3D runtime environment is ready.'
|
||||
Write-Host " PThreads4W: $pthreadsRuntimeDirectory"
|
||||
Write-Host " oneTBB: $oneTBBRuntimeDirectory"
|
||||
Write-Host " Windows SDK: $windowsSdkRuntimeDirectory"
|
||||
Write-Host " Link roots: $($linkDirectories -join '; ')"
|
||||
Write-Host " Vulkan SDK: $vulkanSdkRoot"
|
||||
Write-Host " shaderc: $env:DVZ_SHADERC_RUNTIME_LIBRARY"
|
||||
Write-Host " TEMP: $windowsTempDirectory"
|
||||
|
||||
if ($DatovizBuildDirectory) {
|
||||
$resolvedBuildDirectory = (Resolve-Path -LiteralPath $DatovizBuildDirectory).Path
|
||||
& $CTestExecutable --test-dir $resolvedBuildDirectory -L datoviz --output-on-failure -j 1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
}
|
||||
+181
-101
@@ -293,7 +293,6 @@ public:
|
||||
}
|
||||
};
|
||||
}
|
||||
struct Prepare_Data_Tag {};
|
||||
namespace detail {
|
||||
template <auto Member>
|
||||
struct State_Rely_Key {};
|
||||
@@ -310,6 +309,10 @@ constexpr const void* rely_id() {
|
||||
}
|
||||
struct Root;
|
||||
struct Rely;
|
||||
namespace detail {
|
||||
template <typename Tuple>
|
||||
struct Rely_Build_Storage;
|
||||
}
|
||||
enum class Rely_Error {
|
||||
self_dependency,
|
||||
missing_dependency,
|
||||
@@ -324,7 +327,6 @@ struct Root {
|
||||
};
|
||||
private:
|
||||
using Call = void (*)(Root*);
|
||||
Call prepare_call{};
|
||||
Call exchange_call{};
|
||||
detail::Pmr_Resource pmr_resource;
|
||||
std::pmr::vector<const Rely*> rely_graphs;
|
||||
@@ -344,9 +346,6 @@ private:
|
||||
protected:
|
||||
template <typename Object>
|
||||
void bind_object_crtp() {
|
||||
prepare_call = [](Root* root) {
|
||||
static_cast<Object*>(root)->prepare_data_crtp();
|
||||
};
|
||||
exchange_call = [](Root* root) {
|
||||
static_cast<Object*>(root)->exchange();
|
||||
};
|
||||
@@ -357,9 +356,7 @@ protected:
|
||||
public:
|
||||
Root() : pmr_resource(),
|
||||
rely_graphs(&pmr_resource),
|
||||
dirty_tags(&pmr_resource) {
|
||||
dirty_tags.push_back(detail::rely_id<Prepare_Data_Tag>());
|
||||
}
|
||||
dirty_tags(&pmr_resource) {}
|
||||
[[nodiscard]] std::pmr::memory_resource* memory_resource() const noexcept {
|
||||
return const_cast<detail::Pmr_Resource*>(&pmr_resource);
|
||||
}
|
||||
@@ -385,15 +382,6 @@ public:
|
||||
dirty_tags.erase(current);
|
||||
return true;
|
||||
}
|
||||
void mark_prepare_dirty() {
|
||||
mark_dirty<Prepare_Data_Tag>();
|
||||
}
|
||||
[[nodiscard]] bool need_prepare_data() const {
|
||||
return dirty<Prepare_Data_Tag>();
|
||||
}
|
||||
void prepare_data() {
|
||||
prepare_call(this);
|
||||
}
|
||||
void exchange_object() {
|
||||
exchange_call(this);
|
||||
}
|
||||
@@ -401,8 +389,10 @@ public:
|
||||
struct Builder {
|
||||
using Prop = typename Object::Prop;
|
||||
std::unique_ptr<Object> object;
|
||||
detail::Rely_Build_Storage<typename Object::Relies> rely_storage;
|
||||
template <typename... Args> requires Object_Constructible<Object, Args...>
|
||||
explicit Builder(Args&&... args) : object(std::make_unique<Object>(std::forward<Args>(args)...)) {}
|
||||
explicit Builder(Args&&... args) : object(std::make_unique<Object>(std::forward<Args>(args)...)),
|
||||
rely_storage(object->memory_resource()) {}
|
||||
Builder& set_pmr(Pmr pmr) {
|
||||
object->set_pmr_resource(pmr);
|
||||
return *this;
|
||||
@@ -425,7 +415,7 @@ public:
|
||||
}
|
||||
template <typename... Rely_Tags, typename Callback> requires detail::Rely_Tags_In<typename Object::Relies, Rely_Tags...>
|
||||
Builder& edit_rely(Callback&& callback) {
|
||||
object->template edit_initial_rely<Rely_Tags...>(std::forward<Callback>(callback));
|
||||
rely_storage.template edit<Rely_Tags...>(std::forward<Callback>(callback));
|
||||
return *this;
|
||||
}
|
||||
template <typename Rely_Tag, typename Node_Object> requires
|
||||
@@ -507,19 +497,17 @@ public:
|
||||
}
|
||||
);
|
||||
}
|
||||
std::unique_ptr<Object> build() {
|
||||
std::expected<std::unique_ptr<Object>, Rely_Error> build() {
|
||||
auto check_result = check();
|
||||
if (!check_result) return std::unexpected(check_result.error());
|
||||
rely_storage.commit(object->d.rely_storage);
|
||||
return std::move(object);
|
||||
}
|
||||
std::expected<void, Rely_Error> check() const {
|
||||
return object->check_rely();
|
||||
return rely_storage.check();
|
||||
}
|
||||
};
|
||||
struct Private {
|
||||
template <typename Object>
|
||||
void prepare_data(Object*) {}
|
||||
template <typename Object>
|
||||
void after_prepare_data(Object*) {}
|
||||
};
|
||||
struct Private {};
|
||||
};
|
||||
template <typename T>
|
||||
concept Object_Root =
|
||||
@@ -622,19 +610,20 @@ struct Impl : Base {
|
||||
typename Object::State*,
|
||||
const typename Object::Prop*,
|
||||
const typename Object::State*) {}
|
||||
template <typename Object>
|
||||
void prepare_data(Object*) {}
|
||||
template <typename Object>
|
||||
void after_prepare_data(Object*) {}
|
||||
};
|
||||
using Prev_Private = Private;
|
||||
};
|
||||
struct Rely {
|
||||
using Error = Rely_Error;
|
||||
struct Node {
|
||||
using Bind = void (*)(Node*, Root*);
|
||||
Root* object;
|
||||
std::pmr::vector<Node*> rely;
|
||||
Node(Root* value, std::pmr::memory_resource* resource) : object(value), rely(resource) {}
|
||||
Bind bind;
|
||||
void* data{};
|
||||
mutable void* runtime{};
|
||||
Node(Root* value, std::pmr::memory_resource* resource) : Node(value, resource, nullptr) {}
|
||||
Node(Root* value, std::pmr::memory_resource* resource, Bind value_bind) : object(value), rely(resource), bind(value_bind) {}
|
||||
};
|
||||
struct Edge {
|
||||
Root* target;
|
||||
@@ -703,16 +692,27 @@ public:
|
||||
Rely* edit_rely;
|
||||
const void* target_tag;
|
||||
const void* target_dirty_key;
|
||||
bool bind_node_data;
|
||||
public:
|
||||
Editor(Rely& value, const void* tag, const void* dirty_key) : View(value), edit_rely(&value), target_tag(tag), target_dirty_key(dirty_key) {}
|
||||
Editor(Rely& value, const void* tag, const void* dirty_key) : Editor(value, tag, dirty_key, true) {}
|
||||
Editor(Rely& value, const void* tag, const void* dirty_key, bool value_bind_node_data) : View(value),
|
||||
edit_rely(&value),
|
||||
target_tag(tag),
|
||||
target_dirty_key(dirty_key),
|
||||
bind_node_data(value_bind_node_data) {}
|
||||
void clear() {
|
||||
edit_rely->reset();
|
||||
}
|
||||
Node* add(Root* object) {
|
||||
return edit_rely->add_node(object);
|
||||
template <typename Object> requires std::derived_from<Object, Root>
|
||||
Node* add(Object* object) {
|
||||
return edit_rely->add_node(object, bind_node_data);
|
||||
}
|
||||
template <typename Object> requires std::derived_from<Object, Root>
|
||||
void add(std::initializer_list<Object*> objects) {
|
||||
for (auto* object : objects) edit_rely->add_node(object, bind_node_data);
|
||||
}
|
||||
void add(std::initializer_list<Root*> objects) {
|
||||
for (auto* object : objects) edit_rely->add_node(object);
|
||||
for (auto* object : objects) edit_rely->add_node(object, bind_node_data);
|
||||
}
|
||||
bool remove(Root* object) {
|
||||
return edit_rely->remove_node(object);
|
||||
@@ -735,7 +735,8 @@ public:
|
||||
source,
|
||||
detail::rely_id<detail::State_Rely_Key<Member>>(),
|
||||
target_tag,
|
||||
target_dirty_key
|
||||
target_dirty_key,
|
||||
bind_node_data
|
||||
);
|
||||
}
|
||||
template <typename Buffer_Tag, typename Target, typename Source> requires
|
||||
@@ -747,7 +748,8 @@ public:
|
||||
source,
|
||||
detail::rely_id<detail::Buffer_Rely_Key<Buffer_Tag>>(),
|
||||
target_tag,
|
||||
target_dirty_key
|
||||
target_dirty_key,
|
||||
bind_node_data
|
||||
);
|
||||
}
|
||||
template <typename Source_Tag, typename Target, typename Source> requires
|
||||
@@ -759,7 +761,8 @@ public:
|
||||
source,
|
||||
detail::rely_id<detail::Dirty_Rely_Key<Source_Tag>>(),
|
||||
target_tag,
|
||||
target_dirty_key
|
||||
target_dirty_key,
|
||||
bind_node_data
|
||||
);
|
||||
}
|
||||
template <auto Member, typename Target, typename Source> requires
|
||||
@@ -779,16 +782,22 @@ public:
|
||||
}
|
||||
};
|
||||
private:
|
||||
template <typename Tuple>
|
||||
friend struct detail::Rely_Build_Storage;
|
||||
std::pmr::memory_resource* pmr_resource;
|
||||
std::pmr::list<Node> list;
|
||||
std::pmr::unordered_map<Root*, Node*> nodes;
|
||||
std::pmr::vector<Edge> edges;
|
||||
std::pmr::unordered_multimap<Dirty_Source, Dirty_Target, Dirty_Source_Hash> dirty_edges;
|
||||
std::uint64_t structure_revision{};
|
||||
mutable bool bound{};
|
||||
Node* emplace_node(Root* object) {
|
||||
Node* emplace_node(Root* object, Node::Bind bind) {
|
||||
auto current = nodes.find(object);
|
||||
if (current != nodes.end()) return current->second;
|
||||
list.emplace_back(object, pmr_resource);
|
||||
if (current != nodes.end()) {
|
||||
current->second->bind = bind;
|
||||
return current->second;
|
||||
}
|
||||
list.emplace_back(object, pmr_resource, bind);
|
||||
auto* result = &list.back();
|
||||
nodes.emplace(object, result);
|
||||
return result;
|
||||
@@ -848,20 +857,54 @@ private:
|
||||
Edge edge{target->object, source->object, source_key, target_tag, target_dirty_key};
|
||||
if (std::find(edges.begin(), edges.end(), edge) != edges.end()) return;
|
||||
edges.push_back(edge);
|
||||
++structure_revision;
|
||||
rebuild();
|
||||
}
|
||||
Node* add_node(Root* object) {
|
||||
return emplace_node(object);
|
||||
template <typename Object> requires std::derived_from<Object, Root>
|
||||
static Node::Bind node_bind() {
|
||||
if constexpr (requires(Object* object, Node* node) { object->template bind_rely_node<Object>(node); }) {
|
||||
return [](Node* node, Root* root) {
|
||||
static_cast<Object*>(root)->template bind_rely_node<Object>(node);
|
||||
};
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
void reset() {
|
||||
void bind_nodes() {
|
||||
for (auto& node : list) {
|
||||
if (node.bind) node.bind(&node, node.object);
|
||||
}
|
||||
}
|
||||
template <typename Object> requires std::derived_from<Object, Root>
|
||||
Node* add_node(Object* object, bool bind_node) {
|
||||
auto bind = node_bind<Object>();
|
||||
auto current = nodes.find(object);
|
||||
if (current != nodes.end()) {
|
||||
current->second->bind = bind;
|
||||
if (bind_node && bind) bind(current->second, object);
|
||||
return current->second;
|
||||
}
|
||||
auto* node = emplace_node(object, bind);
|
||||
if (bind_node && bind) bind(node, object);
|
||||
++structure_revision;
|
||||
return node;
|
||||
}
|
||||
void clear_data() {
|
||||
dirty_edges.clear();
|
||||
edges.clear();
|
||||
nodes.clear();
|
||||
list.clear();
|
||||
}
|
||||
void reset() {
|
||||
if (list.empty() && edges.empty()) return;
|
||||
clear_data();
|
||||
++structure_revision;
|
||||
}
|
||||
void copy_from(const Rely& other) {
|
||||
for (const auto& node : other.list) emplace_node(node.object);
|
||||
for (const auto& node : other.list) emplace_node(node.object, node.bind)->data = node.data;
|
||||
edges = other.edges;
|
||||
structure_revision = other.structure_revision;
|
||||
rebuild();
|
||||
}
|
||||
void swap_data(Rely& other) noexcept {
|
||||
@@ -869,6 +912,7 @@ private:
|
||||
nodes.swap(other.nodes);
|
||||
edges.swap(other.edges);
|
||||
dirty_edges.swap(other.dirty_edges);
|
||||
std::swap(structure_revision, other.structure_revision);
|
||||
}
|
||||
template <typename Target, typename Source>
|
||||
Node* add_rely_runtime(
|
||||
@@ -876,9 +920,10 @@ private:
|
||||
Source* source,
|
||||
const void* source_key,
|
||||
const void* target_tag,
|
||||
const void* target_dirty_key) {
|
||||
auto* target_node = add_node(target);
|
||||
auto* source_node = add_node(source);
|
||||
const void* target_dirty_key,
|
||||
bool bind_node) {
|
||||
auto* target_node = add_node(target, bind_node);
|
||||
auto* source_node = add_node(source, bind_node);
|
||||
add_edge(target_node, source_node, source_key, target_tag, target_dirty_key);
|
||||
return source_node;
|
||||
}
|
||||
@@ -894,6 +939,7 @@ private:
|
||||
}
|
||||
);
|
||||
if (old_size == edges.size()) return false;
|
||||
++structure_revision;
|
||||
rebuild();
|
||||
return true;
|
||||
}
|
||||
@@ -914,28 +960,38 @@ private:
|
||||
return &value == node;
|
||||
}
|
||||
);
|
||||
++structure_revision;
|
||||
rebuild();
|
||||
return true;
|
||||
}
|
||||
void clear_rely_impl(Root* target) {
|
||||
auto old_size = edges.size();
|
||||
std::erase_if(edges, [&](const Edge& edge) {
|
||||
return edge.target == target;
|
||||
});
|
||||
if (old_size == edges.size()) return;
|
||||
++structure_revision;
|
||||
rebuild();
|
||||
}
|
||||
void remove_rely_to_impl(Root* source) {
|
||||
auto old_size = edges.size();
|
||||
std::erase_if(edges, [&](const Edge& edge) {
|
||||
return edge.source == source;
|
||||
});
|
||||
if (old_size == edges.size()) return;
|
||||
++structure_revision;
|
||||
rebuild();
|
||||
}
|
||||
void disconnect_impl(Root* object) {
|
||||
auto old_size = edges.size();
|
||||
std::erase_if(
|
||||
edges,
|
||||
[&](const Edge& edge) {
|
||||
return edge.target == object || edge.source == object;
|
||||
}
|
||||
);
|
||||
if (old_size == edges.size()) return;
|
||||
++structure_revision;
|
||||
rebuild();
|
||||
}
|
||||
bool remove_rely_impl(Root* target, Root* source) {
|
||||
@@ -947,6 +1003,7 @@ private:
|
||||
}
|
||||
);
|
||||
if (old_size == edges.size()) return false;
|
||||
++structure_revision;
|
||||
rebuild();
|
||||
return true;
|
||||
}
|
||||
@@ -968,7 +1025,7 @@ public:
|
||||
Rely& operator=(const Rely& other) {
|
||||
if (this == &other) return *this;
|
||||
unbind();
|
||||
reset();
|
||||
clear_data();
|
||||
copy_from(other);
|
||||
return *this;
|
||||
}
|
||||
@@ -988,8 +1045,8 @@ public:
|
||||
else {
|
||||
Rely this_value(*this, other.pmr_resource);
|
||||
Rely other_value(other, pmr_resource);
|
||||
reset();
|
||||
other.reset();
|
||||
clear_data();
|
||||
other.clear_data();
|
||||
copy_from(other_value);
|
||||
other.copy_from(this_value);
|
||||
}
|
||||
@@ -1015,6 +1072,9 @@ public:
|
||||
std::pmr::memory_resource* memory_resource() const noexcept {
|
||||
return pmr_resource;
|
||||
}
|
||||
std::uint64_t revision() const noexcept {
|
||||
return structure_revision;
|
||||
}
|
||||
Node* find(Root* object) {
|
||||
auto current = nodes.find(object);
|
||||
if (current == nodes.end()) return nullptr;
|
||||
@@ -1120,14 +1180,6 @@ public:
|
||||
}
|
||||
);
|
||||
}
|
||||
std::expected<void, Error> execute_prepare() const {
|
||||
return for_each_dag(
|
||||
[](const Node& node) {
|
||||
if (!node.object->take_dirty<Prepare_Data_Tag>()) return;
|
||||
node.object->prepare_data();
|
||||
}
|
||||
);
|
||||
}
|
||||
void emit(Root* source, const void* key) const {
|
||||
auto [first, last] = dirty_edges.equal_range(Dirty_Source{source, key});
|
||||
for (; first != last; ++first) {
|
||||
@@ -1226,6 +1278,72 @@ public:
|
||||
);
|
||||
}
|
||||
};
|
||||
template <Rely_Mechanism... Relies>
|
||||
struct Rely_Build_Storage<std::tuple<Relies...>> {
|
||||
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
|
||||
private:
|
||||
std::tuple<std::conditional_t<true, Rely, Relies>...> relies;
|
||||
template <typename Tag>
|
||||
static consteval std::size_t index() {
|
||||
return tag_index<Tag, Relies...>();
|
||||
}
|
||||
template <std::size_t I = 0>
|
||||
std::expected<void, Rely_Error> check_impl() const {
|
||||
if constexpr (I == sizeof...(Relies)) {
|
||||
return {};
|
||||
}
|
||||
else {
|
||||
auto result = std::get<I>(relies).dag();
|
||||
if (!result) return std::unexpected(result.error());
|
||||
return check_impl<I + 1>();
|
||||
}
|
||||
}
|
||||
template <typename Rely_Type>
|
||||
void commit_one(Rely_Storage<std::tuple<Relies...>>& storage) {
|
||||
using Tag = typename Rely_Type::Tag_Type;
|
||||
auto& buffer = storage.template get<Tag>();
|
||||
buffer.cache->swap(get<Tag>());
|
||||
buffer.cache->bind_nodes();
|
||||
*buffer.render = *buffer.cache;
|
||||
buffer.render->bind();
|
||||
}
|
||||
public:
|
||||
Rely_Build_Storage() : Rely_Build_Storage(std::pmr::get_default_resource()) {}
|
||||
explicit Rely_Build_Storage(std::pmr::memory_resource* resource) : relies(std::allocator_arg, allocator_type{resource}) {}
|
||||
template <typename Tag> requires Rely_Tag_In<Tag, std::tuple<Relies...>>
|
||||
Rely& get() {
|
||||
return std::get<index<Tag>()>(relies);
|
||||
}
|
||||
template <typename Tag> requires Rely_Tag_In<Tag, std::tuple<Relies...>>
|
||||
const Rely& get() const {
|
||||
return std::get<index<Tag>()>(relies);
|
||||
}
|
||||
template <typename... Tags, typename Callback> requires
|
||||
Rely_Tags_In<std::tuple<Relies...>, Tags...> &&
|
||||
Rely_Edit_Callback_For<Callback, Tags...>
|
||||
void edit(Callback&& callback) {
|
||||
auto editors = std::tuple{
|
||||
Rely::Editor(
|
||||
get<Tags>(),
|
||||
rely_id<Tags>(),
|
||||
rely_id<Dirty_Rely_Key<Tags>>(),
|
||||
false
|
||||
)...
|
||||
};
|
||||
std::apply(
|
||||
[&](auto&... values) {
|
||||
std::invoke(std::forward<Callback>(callback), values...);
|
||||
},
|
||||
editors
|
||||
);
|
||||
}
|
||||
std::expected<void, Rely_Error> check() const {
|
||||
return check_impl();
|
||||
}
|
||||
void commit(Rely_Storage<std::tuple<Relies...>>& storage) {
|
||||
(commit_one<Relies>(storage), ...);
|
||||
}
|
||||
};
|
||||
template <typename... Tags>
|
||||
using Rely_Copy_Tuple = std::tuple<std::conditional_t<true, Rely, Tags>...>;
|
||||
}
|
||||
@@ -1236,6 +1354,7 @@ struct Attach_Object : Obj {
|
||||
using Buffers = typename Obj::Buffers;
|
||||
using Relies = typename Obj::Relies;
|
||||
using Builder = typename Obj::template Builder<Attach_Object>;
|
||||
using Attached_Object = Obj;
|
||||
struct Private : Obj::Private, Double_Buffer<Prop> {
|
||||
using Allocator = std::pmr::polymorphic_allocator<std::byte>;
|
||||
Double_Buffer<State> state;
|
||||
@@ -1299,14 +1418,6 @@ private:
|
||||
};
|
||||
walk_private<true, Obj>(callback);
|
||||
}
|
||||
void prepare_data_crtp() {
|
||||
auto& private_data = static_cast<typename Obj::Private&>(d);
|
||||
private_data.template prepare_data<Attach_Object>(this);
|
||||
auto callback = [&](auto& value) {
|
||||
value.template after_prepare_data<Attach_Object>(this);
|
||||
};
|
||||
walk_private<true, Obj>(callback);
|
||||
}
|
||||
template <typename Tuple, std::size_t I = 0>
|
||||
static std::expected<void, Rely_Error> check_rely_tuple(const Tuple& tuple) {
|
||||
if constexpr (I == std::tuple_size_v<Tuple>) {
|
||||
@@ -1326,7 +1437,8 @@ private:
|
||||
Rely::Editor(
|
||||
std::get<I>(values),
|
||||
detail::rely_id<Tags>(),
|
||||
detail::rely_id<detail::Dirty_Rely_Key<Tags>>()
|
||||
detail::rely_id<detail::Dirty_Rely_Key<Tags>>(),
|
||||
true
|
||||
)...
|
||||
};
|
||||
}
|
||||
@@ -1336,25 +1448,6 @@ private:
|
||||
std::index_sequence<I...>) {
|
||||
(d.rely_storage.template get<Tags>().cache->swap(std::get<I>(values)), ...);
|
||||
}
|
||||
template <typename... Tags>
|
||||
void sync_initial_rely() {
|
||||
auto sync_one = [&]<typename Tag>() {
|
||||
auto& buffer = d.rely_storage.template get<Tag>();
|
||||
*buffer.render = *buffer.cache;
|
||||
buffer.render->bind();
|
||||
};
|
||||
(sync_one.template operator()<Tags>(), ...);
|
||||
}
|
||||
template <typename... Tags>
|
||||
auto make_initial_rely_editors() {
|
||||
return std::tuple{
|
||||
Rely::Editor(
|
||||
*d.rely_storage.template get<Tags>().cache,
|
||||
detail::rely_id<Tags>(),
|
||||
detail::rely_id<detail::Dirty_Rely_Key<Tags>>()
|
||||
)...
|
||||
};
|
||||
}
|
||||
void exchange_unlocked() {
|
||||
before_exchange();
|
||||
static_cast<Double_Buffer<Prop>&>(d).exchange();
|
||||
@@ -1406,19 +1499,6 @@ public:
|
||||
commit_cache_rely<Tags...>(next, std::index_sequence_for<Tags...>{});
|
||||
return {};
|
||||
}
|
||||
template <typename... Tags, typename Callback> requires
|
||||
detail::Rely_Tags_In<Relies, Tags...> &&
|
||||
detail::Rely_Edit_Callback_For<Callback, Tags...>
|
||||
void edit_initial_rely(Callback&& callback) {
|
||||
auto editors = make_initial_rely_editors<Tags...>();
|
||||
std::apply(
|
||||
[&](auto&... values) {
|
||||
std::invoke(std::forward<Callback>(callback), values...);
|
||||
},
|
||||
editors
|
||||
);
|
||||
sync_initial_rely<Tags...>();
|
||||
}
|
||||
std::expected<void, Rely_Error> check_rely() const {
|
||||
return d.rely_storage.check_cache();
|
||||
}
|
||||
|
||||
+314
-146
@@ -1,26 +1,161 @@
|
||||
#include "base.hpp"
|
||||
#include <oneapi/tbb/flow_graph.h>
|
||||
namespace aethera::_2D {
|
||||
struct Prepare_Data_Tag {};
|
||||
struct Paint_Tag {};
|
||||
struct Renderable;
|
||||
struct Prepare_Data {
|
||||
using Message = oneapi::tbb::flow::continue_msg;
|
||||
using Async_Node = oneapi::tbb::flow::async_node<Message, Message>;
|
||||
using Gateway = typename Async_Node::gateway_type;
|
||||
struct Node_Data {
|
||||
void (*prepare)(Root*, Gateway&){};
|
||||
Renderable* renderable{};
|
||||
};
|
||||
private:
|
||||
Node_Data node_data;
|
||||
template <bool Reverse, typename Layer, typename Object, typename Callback>
|
||||
static void walk_private(Object* object, Callback& callback) {
|
||||
if constexpr (requires { typename Layer::Prev_Object; }) {
|
||||
if constexpr (!Reverse) walk_private<Reverse, typename Layer::Prev_Object>(object, callback);
|
||||
callback(static_cast<typename Layer::Private&>(object->d));
|
||||
if constexpr (Reverse) walk_private<Reverse, typename Layer::Prev_Object>(object, callback);
|
||||
}
|
||||
}
|
||||
protected:
|
||||
template <typename Object>
|
||||
static void execute(Object* object) {
|
||||
auto& private_data = static_cast<typename Object::Private&>(object->d);
|
||||
if constexpr (requires { private_data.template prepare_data<Object>(object); }) {
|
||||
private_data.template prepare_data<Object>(object);
|
||||
}
|
||||
auto callback = [&](auto& value) {
|
||||
if constexpr (requires { value.template after_prepare_data<Object>(object); }) {
|
||||
value.template after_prepare_data<Object>(object);
|
||||
}
|
||||
};
|
||||
walk_private<true, typename Object::Attached_Object>(object, callback);
|
||||
}
|
||||
Node_Data& prepare_node_data() {
|
||||
return node_data;
|
||||
}
|
||||
public:
|
||||
template <typename Object>
|
||||
void bind_rely_node(Rely::Node* node) {
|
||||
if (!node_data.prepare) {
|
||||
auto* object = static_cast<Object*>(this);
|
||||
object->template mark_dirty<Prepare_Data_Tag>();
|
||||
node_data.prepare = [](Root* root, Gateway& gateway) {
|
||||
execute(static_cast<Object*>(root));
|
||||
(void)gateway.try_put(Message{});
|
||||
};
|
||||
}
|
||||
node->data = &node_data;
|
||||
}
|
||||
};
|
||||
struct Color_Cache {};
|
||||
struct Renderable : Impl<Renderable, Root, Tagged_Buffer<Color_Cache>> {
|
||||
struct Secene;
|
||||
struct Renderable : Impl<Renderable, Root, Tagged_Buffer<Color_Cache>>, Prepare_Data {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
private:
|
||||
using Message = oneapi::tbb::flow::continue_msg;
|
||||
using Async_Node = oneapi::tbb::flow::async_node<Message, Message>;
|
||||
using Gateway = typename Async_Node::gateway_type;
|
||||
using Task_Call = void (*)(void*, Gateway&);
|
||||
using Task_State = std::tuple<void*, Task_Call>;
|
||||
public:
|
||||
struct Private : Prev_Private {
|
||||
template <auto Callback, typename Context> requires std::invocable<decltype(Callback), Context*>
|
||||
static void set_sync(Double_Buffer<Task_State>& buffer, Context* context, bool initial) {
|
||||
Task_State task{
|
||||
context,
|
||||
[](void* value, Gateway& gateway) {
|
||||
std::invoke(Callback, static_cast<Context*>(value));
|
||||
(void)gateway.try_put(Message{});
|
||||
}
|
||||
};
|
||||
*buffer.cache = task;
|
||||
if (initial) *buffer.render = task;
|
||||
}
|
||||
template <auto Callback, typename Context> requires std::invocable<decltype(Callback), Context*, Gateway&>
|
||||
static void set_async(Double_Buffer<Task_State>& buffer, Context* context, bool initial) {
|
||||
Task_State task{
|
||||
context,
|
||||
[](void* value, Gateway& gateway) {
|
||||
gateway.reserve_wait();
|
||||
std::invoke(Callback, static_cast<Context*>(value), gateway);
|
||||
}
|
||||
};
|
||||
*buffer.cache = task;
|
||||
if (initial) *buffer.render = task;
|
||||
}
|
||||
static void publish(Double_Buffer<Task_State>& buffer) {
|
||||
Task_State value = *buffer.cache;
|
||||
buffer.exchange();
|
||||
*buffer.cache = value;
|
||||
}
|
||||
template <typename Object>
|
||||
static void prepare(Object* object) {
|
||||
Prepare_Data::execute(object);
|
||||
}
|
||||
template <typename Object>
|
||||
static void bind(Renderable* renderable, Object* object) {
|
||||
renderable->paint_call = [](Renderable* value) {
|
||||
auto* current = static_cast<Object*>(value);
|
||||
auto& private_data = static_cast<typename Object::Private&>(current->d);
|
||||
private_data.template paint<Object>(current);
|
||||
};
|
||||
set_sync<&Private::template prepare<Object>>(renderable->prepare_task, object, true);
|
||||
set_sync<&Object::paint>(renderable->paint_task, object, true);
|
||||
}
|
||||
template <typename Object>
|
||||
void prepare_data(Object*) {}
|
||||
template <typename Object>
|
||||
void after_prepare_data(Object* object) {
|
||||
static_cast<Renderable*>(object)->paint_dirty = true;
|
||||
}
|
||||
template <typename Object>
|
||||
void after_exchange(
|
||||
Object* object,
|
||||
typename Object::Prop*,
|
||||
typename Object::State*,
|
||||
const typename Object::Prop*,
|
||||
const typename Object::State*) {
|
||||
auto* renderable = static_cast<Renderable*>(object);
|
||||
publish(renderable->prepare_task);
|
||||
publish(renderable->paint_task);
|
||||
}
|
||||
template <typename Object>
|
||||
void paint(Object*) {}
|
||||
};
|
||||
private:
|
||||
using Call = void (*)(Renderable*);
|
||||
Call paint_call{};
|
||||
bool paint_dirty{true};
|
||||
Double_Buffer<Task_State> prepare_task;
|
||||
Double_Buffer<Task_State> paint_task;
|
||||
friend struct Secene;
|
||||
protected:
|
||||
template <typename Object>
|
||||
void bind_object_crtp() {
|
||||
Prev_Object::template bind_object_crtp<Object>();
|
||||
paint_call = [](Renderable* renderable) {
|
||||
auto* object = static_cast<Object*>(renderable);
|
||||
auto& private_data = static_cast<typename Object::Private&>(object->d);
|
||||
private_data.template paint<Object>(object);
|
||||
};
|
||||
Private::bind(this, static_cast<Object*>(this));
|
||||
}
|
||||
public:
|
||||
template <typename Object>
|
||||
void bind_rely_node(Rely::Node* node) {
|
||||
Prepare_Data::template bind_rely_node<Object>(node);
|
||||
prepare_node_data().renderable = this;
|
||||
}
|
||||
void mark_prepare_dirty() {
|
||||
mark_dirty<Prepare_Data_Tag>();
|
||||
}
|
||||
[[nodiscard]] bool need_prepare_data() const {
|
||||
return dirty<Prepare_Data_Tag>();
|
||||
}
|
||||
void mark_paint_dirty() {
|
||||
paint_dirty = true;
|
||||
}
|
||||
@@ -32,84 +167,155 @@ public:
|
||||
void paint() {
|
||||
paint_call(this);
|
||||
}
|
||||
struct Private : Prev_Private {
|
||||
template <typename Object>
|
||||
void after_prepare_data(Object* object) {
|
||||
object->mark_paint_dirty();
|
||||
}
|
||||
template <typename Object>
|
||||
void paint(Object*) {}
|
||||
};
|
||||
};
|
||||
struct Layout_Tag {};
|
||||
struct Extra_Tag {};
|
||||
struct Secene : Impl<
|
||||
Secene,
|
||||
Root,
|
||||
Rely_Type<Prepare_Data_Tag>,
|
||||
Rely_Type<Layout_Tag>,
|
||||
Rely_Type<Extra_Tag>
|
||||
> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {};
|
||||
private:
|
||||
using Call = void (*)(const Secene*);
|
||||
Call prepare_execute_call{};
|
||||
Call paint_execute_call{};
|
||||
std::pmr::vector<Renderable*> renderables;
|
||||
std::pmr::unordered_map<Root*, Renderable*> renderable_nodes;
|
||||
void bind_renderables() {
|
||||
renderable_nodes.clear();
|
||||
for (auto* object : renderables) renderable_nodes.emplace(static_cast<Root*>(object), object);
|
||||
template <auto Callback, typename Context> requires std::invocable<decltype(Callback), Context*>
|
||||
void set_prepare_task(Context* context) {
|
||||
Private::template set_sync<Callback>(prepare_task, context, false);
|
||||
}
|
||||
protected:
|
||||
template <typename Object>
|
||||
void bind_object_crtp() {
|
||||
Prev_Object::template bind_object_crtp<Object>();
|
||||
prepare_execute_call = [](const Secene* scene) {
|
||||
auto* object = static_cast<const Object*>(scene);
|
||||
(void)object->template render_rely<Prepare_Data_Tag>().execute_prepare();
|
||||
};
|
||||
paint_execute_call = [](const Secene* scene) {
|
||||
auto* object = static_cast<const Object*>(scene);
|
||||
(void)object->template render_rely<Prepare_Data_Tag>().for_each_dag(
|
||||
[&](const auto& node) {
|
||||
auto current = scene->renderable_nodes.find(node.object);
|
||||
if (current == scene->renderable_nodes.end()) return;
|
||||
auto* renderable = current->second;
|
||||
if (!renderable->take_paint_dirty()) return;
|
||||
renderable->paint();
|
||||
}
|
||||
);
|
||||
};
|
||||
template <auto Callback, typename Context> requires std::invocable<decltype(Callback), Context*, Gateway&>
|
||||
void set_prepare_async_task(Context* context) {
|
||||
Private::template set_async<Callback>(prepare_task, context, false);
|
||||
}
|
||||
public:
|
||||
Secene() : renderables(memory_resource()),
|
||||
renderable_nodes(memory_resource()) {}
|
||||
void execute_prepare() const {
|
||||
prepare_execute_call(this);
|
||||
template <auto Callback, typename Context> requires std::invocable<decltype(Callback), Context*>
|
||||
void set_paint_task(Context* context) {
|
||||
Private::template set_sync<Callback>(paint_task, context, false);
|
||||
}
|
||||
void execute_paint() const {
|
||||
paint_execute_call(this);
|
||||
template <auto Callback, typename Context> requires std::invocable<decltype(Callback), Context*, Gateway&>
|
||||
void set_paint_async_task(Context* context) {
|
||||
Private::template set_async<Callback>(paint_task, context, false);
|
||||
}
|
||||
template <typename Object>
|
||||
struct Builder : Prev_Builder<Object> {
|
||||
using Base = Prev_Builder<Object>;
|
||||
using Base::Base;
|
||||
template <typename Render_Object> requires std::derived_from<Render_Object, Renderable>
|
||||
Builder& add_renderable(std::initializer_list<Render_Object*> list) {
|
||||
for (auto* object : list) {
|
||||
this->object->renderables.push_back(object);
|
||||
this->template add_rely_node<Prepare_Data_Tag>(object);
|
||||
}
|
||||
template <auto Callback> requires std::invocable<decltype(Callback), Object*>
|
||||
Builder& set_prepare_task() {
|
||||
auto* renderable = static_cast<Renderable*>(this->object.get());
|
||||
Private::template set_sync<Callback>(renderable->prepare_task, this->object.get(), true);
|
||||
return *this;
|
||||
}
|
||||
std::unique_ptr<Object> build() {
|
||||
this->object->bind_renderables();
|
||||
return std::move(this->object);
|
||||
template <auto Callback> requires std::invocable<decltype(Callback), Object*, Gateway&>
|
||||
Builder& set_prepare_async_task() {
|
||||
auto* renderable = static_cast<Renderable*>(this->object.get());
|
||||
Private::template set_async<Callback>(renderable->prepare_task, this->object.get(), true);
|
||||
return *this;
|
||||
}
|
||||
template <auto Callback> requires std::invocable<decltype(Callback), Object*>
|
||||
Builder& set_paint_task() {
|
||||
auto* renderable = static_cast<Renderable*>(this->object.get());
|
||||
Private::template set_sync<Callback>(renderable->paint_task, this->object.get(), true);
|
||||
return *this;
|
||||
}
|
||||
template <auto Callback> requires std::invocable<decltype(Callback), Object*, Gateway&>
|
||||
Builder& set_paint_async_task() {
|
||||
auto* renderable = static_cast<Renderable*>(this->object.get());
|
||||
Private::template set_async<Callback>(renderable->paint_task, this->object.get(), true);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
};
|
||||
struct Secene : Impl<Secene, Root, Rely_Type<Prepare_Data_Tag>, Rely_Type<Paint_Tag>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {};
|
||||
struct Private : Prev_Private {
|
||||
using Message = oneapi::tbb::flow::continue_msg;
|
||||
using Async_Node = oneapi::tbb::flow::async_node<Message, Message>;
|
||||
template <typename Tag>
|
||||
struct Stage : Pinned {
|
||||
struct Node : Pinned {
|
||||
using Gate = oneapi::tbb::flow::continue_node<Message>;
|
||||
static auto body(Root* object, Prepare_Data::Node_Data* data) requires std::same_as<Tag, Prepare_Data_Tag> {
|
||||
return [object, data](const Message&, typename Async_Node::gateway_type& gateway) {
|
||||
if (!object->take_dirty<Prepare_Data_Tag>()) {
|
||||
(void)gateway.try_put(Message{});
|
||||
return;
|
||||
}
|
||||
if (!data->renderable) {
|
||||
data->prepare(object, gateway);
|
||||
return;
|
||||
}
|
||||
auto& task = *data->renderable->prepare_task.render;
|
||||
std::get<1>(task)(std::get<0>(task), gateway);
|
||||
};
|
||||
}
|
||||
static auto body(Root*, Prepare_Data::Node_Data* data) requires std::same_as<Tag, Paint_Tag> {
|
||||
return [renderable = data->renderable](const Message&, typename Async_Node::gateway_type& gateway) {
|
||||
if (!renderable->take_paint_dirty()) {
|
||||
(void)gateway.try_put(Message{});
|
||||
return;
|
||||
}
|
||||
auto& task = *renderable->paint_task.render;
|
||||
std::get<1>(task)(std::get<0>(task), gateway);
|
||||
};
|
||||
}
|
||||
Gate gate;
|
||||
Async_Node execute;
|
||||
Node(oneapi::tbb::flow::graph& graph, Root* object, Prepare_Data::Node_Data* data) :
|
||||
gate(
|
||||
graph,
|
||||
[](const Message&) {
|
||||
return Message{};
|
||||
}
|
||||
),
|
||||
execute(graph, oneapi::tbb::flow::unlimited, body(object, data)) {
|
||||
oneapi::tbb::flow::make_edge(gate, execute);
|
||||
}
|
||||
};
|
||||
oneapi::tbb::flow::graph graph;
|
||||
oneapi::tbb::flow::broadcast_node<Message> start;
|
||||
std::pmr::list<Node> nodes;
|
||||
std::uint64_t revision;
|
||||
template <typename Object>
|
||||
Stage(const Object* object, std::pmr::memory_resource* resource) :
|
||||
start(graph),
|
||||
nodes(resource),
|
||||
revision(object->template render_rely<Tag>().revision()) {
|
||||
(void)object->template for_each_rely_dag<Tag>(
|
||||
[&](const Rely::View& view, const Rely::Node& rely_node) {
|
||||
auto* data = static_cast<Prepare_Data::Node_Data*>(rely_node.data);
|
||||
nodes.emplace_back(graph, rely_node.object, data);
|
||||
auto* node = &nodes.back();
|
||||
rely_node.runtime = node;
|
||||
auto dependencies = view.get_rely(rely_node.object);
|
||||
if (dependencies.empty()) oneapi::tbb::flow::make_edge(start, node->gate);
|
||||
for (const auto* dependency : dependencies) {
|
||||
auto* source = static_cast<Node*>(dependency->runtime);
|
||||
oneapi::tbb::flow::make_edge(source->execute, node->gate);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
void run() {
|
||||
(void)start.try_put(Message{});
|
||||
graph.wait_for_all();
|
||||
}
|
||||
};
|
||||
template <typename Object>
|
||||
static void update_stages(const Object* object) {
|
||||
const auto& prepare_rely = object->template render_rely<Prepare_Data_Tag>();
|
||||
const auto& paint_rely = object->template render_rely<Paint_Tag>();
|
||||
if (
|
||||
object->prepare_stage &&
|
||||
object->paint_stage &&
|
||||
object->prepare_stage->revision == prepare_rely.revision() &&
|
||||
object->paint_stage->revision == paint_rely.revision()) return;
|
||||
if (object->prepare_stage) object->prepare_stage->graph.wait_for_all();
|
||||
if (object->paint_stage) object->paint_stage->graph.wait_for_all();
|
||||
auto prepare = std::make_unique<Stage<Prepare_Data_Tag>>(object, object->memory_resource());
|
||||
auto paint = std::make_unique<Stage<Paint_Tag>>(object, object->memory_resource());
|
||||
object->prepare_stage = std::move(prepare);
|
||||
object->paint_stage = std::move(paint);
|
||||
}
|
||||
template <typename Object>
|
||||
static void execute_prepare(const Object* object) {
|
||||
if (!object->prepare_stage || !object->paint_stage) update_stages(object);
|
||||
object->prepare_stage->run();
|
||||
}
|
||||
template <typename Object>
|
||||
static void execute_paint(const Object* object) {
|
||||
if (!object->prepare_stage || !object->paint_stage) update_stages(object);
|
||||
object->prepare_stage->run();
|
||||
object->paint_stage->run();
|
||||
}
|
||||
template <typename Object>
|
||||
void after_exchange(
|
||||
Object* object,
|
||||
@@ -121,87 +327,49 @@ public:
|
||||
object->for_each_render_rely(
|
||||
[&](const Rely& rely) {
|
||||
rely.for_each(
|
||||
[&](const auto& node) {
|
||||
[&](const Rely::Node& node) {
|
||||
if (exchanged.insert(node.object).second) node.object->exchange_object();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
update_stages(object);
|
||||
}
|
||||
};
|
||||
private:
|
||||
using Call = void (*)(const Secene*);
|
||||
Call prepare_execute_call{};
|
||||
Call paint_execute_call{};
|
||||
mutable std::unique_ptr<typename Private::template Stage<Prepare_Data_Tag>> prepare_stage;
|
||||
mutable std::unique_ptr<typename Private::template Stage<Paint_Tag>> paint_stage;
|
||||
protected:
|
||||
template <typename Object>
|
||||
void bind_object_crtp() {
|
||||
Prev_Object::template bind_object_crtp<Object>();
|
||||
prepare_execute_call = [](const Secene* scene) {
|
||||
Private::execute_prepare(static_cast<const Object*>(scene));
|
||||
};
|
||||
paint_execute_call = [](const Secene* scene) {
|
||||
Private::execute_paint(static_cast<const Object*>(scene));
|
||||
};
|
||||
}
|
||||
public:
|
||||
void execute_prepare() const {
|
||||
prepare_execute_call(this);
|
||||
}
|
||||
void execute_paint() const {
|
||||
paint_execute_call(this);
|
||||
}
|
||||
template <typename Object>
|
||||
struct Builder : Prev_Builder<Object> {
|
||||
using Base = Prev_Builder<Object>;
|
||||
using Base::Base;
|
||||
std::expected<std::unique_ptr<Object>, Rely_Error> build() {
|
||||
auto result = Base::build();
|
||||
if (!result) return std::unexpected(result.error());
|
||||
Private::update_stages(result->get());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
struct SEP_root : aethera::Root {};
|
||||
struct SEP : aethera::Impl<SEP, SEP_root, aethera::Tagged_Buffer<std::vector<int>>> {
|
||||
struct Prop : Prev_Prop {
|
||||
int value{};
|
||||
};
|
||||
struct State : Prev_State {
|
||||
int a{};
|
||||
};
|
||||
struct Private : Prev_Private {
|
||||
template <typename Object>
|
||||
void before_exchange(
|
||||
Object*,
|
||||
typename Object::Prop*,
|
||||
typename Object::State* inside_state,
|
||||
const typename Object::Prop*,
|
||||
const typename Object::State*) {
|
||||
++inside_state->a;
|
||||
}
|
||||
};
|
||||
};
|
||||
using SEP_2 = aethera::Attach_Object<SEP>;
|
||||
struct SEP_Child : aethera::Impl<SEP_Child, SEP> {
|
||||
struct Prop : Prev_Prop {
|
||||
int child_value{};
|
||||
};
|
||||
struct State : Prev_State {
|
||||
int b{};
|
||||
};
|
||||
struct Private : Prev_Private {
|
||||
template <typename Object>
|
||||
void before_exchange(
|
||||
Object*,
|
||||
typename Object::Prop*,
|
||||
typename Object::State* inside_state,
|
||||
const typename Object::Prop*,
|
||||
const typename Object::State*) {
|
||||
inside_state->b += 10;
|
||||
}
|
||||
};
|
||||
};
|
||||
using SEP_Child_2 = aethera::Attach_Object<SEP_Child>;
|
||||
struct Data_Cache {
|
||||
int value{};
|
||||
};
|
||||
struct Data_Object : aethera::Impl<Data_Object, SEP_root, aethera::Tagged_Buffer<Data_Cache>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {
|
||||
int value{};
|
||||
int other{};
|
||||
};
|
||||
int id;
|
||||
std::vector<int>* order;
|
||||
Data_Object(int id, std::vector<int>* order) : id(id), order(order) {}
|
||||
struct Private : Prev_Private {
|
||||
int previous_value{};
|
||||
template <typename Object, typename Owner, typename Member>
|
||||
void before_state_set(Object*, Member Owner::* member, typename Object::State* state) {
|
||||
if constexpr (std::same_as<decltype(member), decltype(&Data_Object::State::value)>) {
|
||||
if (member == &Data_Object::State::value) previous_value = state->*member;
|
||||
}
|
||||
}
|
||||
template <typename Object, typename Owner, typename Member>
|
||||
void after_state_set(Object* object, Member Owner::* member, typename Object::State* state) {
|
||||
if constexpr (std::same_as<decltype(member), decltype(&Data_Object::State::value)>) {
|
||||
if (member == &Data_Object::State::value && state->*member != previous_value) object->mark_prepare_dirty();
|
||||
}
|
||||
}
|
||||
template <typename Object>
|
||||
void prepare_data(Object* object) {
|
||||
object->order->push_back(object->id);
|
||||
object->template buffer<Data_Cache>().value = object->id;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,390 +1,4 @@
|
||||
#include "../kernel/render_2D.hpp"
|
||||
using Data_Object_2 = aethera::Attach_Object<Data_Object>;
|
||||
struct Test_Cache {
|
||||
int value{};
|
||||
};
|
||||
struct Test_Cache_Tag {};
|
||||
struct Test_Renderable : aethera::Impl<
|
||||
Test_Renderable,
|
||||
aethera::_2D::Renderable,
|
||||
aethera::Tagged_Buffer<Test_Cache>,
|
||||
aethera::Tagged_Buffer<Test_Cache_Tag, Test_Cache>
|
||||
> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {
|
||||
int value{};
|
||||
int other{};
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
int id;
|
||||
std::vector<int>* order;
|
||||
Test_Renderable(int id, std::vector<int>* order) : id(id), order(order) {}
|
||||
struct Private : Prev_Private {
|
||||
int previous_value{};
|
||||
template <typename Object, typename Owner, typename Member>
|
||||
void before_state_set(Object*, Member Owner::* member, typename Object::State* state) {
|
||||
if constexpr (std::same_as<decltype(member), decltype(&Test_Renderable::State::value)>) {
|
||||
if (member == &Test_Renderable::State::value) previous_value = state->*member;
|
||||
}
|
||||
}
|
||||
template <typename Object, typename Owner, typename Member>
|
||||
void after_state_set(Object* object, Member Owner::* member, typename Object::State* state) {
|
||||
if constexpr (std::same_as<decltype(member), decltype(&Test_Renderable::State::value)>) {
|
||||
if (member == &Test_Renderable::State::value && state->*member != previous_value) object->mark_prepare_dirty();
|
||||
}
|
||||
}
|
||||
template <typename Object>
|
||||
void prepare_data(Object* object) {
|
||||
object->order->push_back(object->id);
|
||||
object->template buffer<Test_Cache>().value = object->id;
|
||||
}
|
||||
template <typename Object>
|
||||
void paint(Object* object) {
|
||||
object->order->push_back(object->id * 10);
|
||||
}
|
||||
};
|
||||
};
|
||||
using Test_Renderable_2 = aethera::Attach_Object<Test_Renderable>;
|
||||
struct Pmr_Buffer_Tag {};
|
||||
struct Pmr_Object : aethera::Impl<
|
||||
Pmr_Object,
|
||||
SEP_root,
|
||||
aethera::Tagged_Buffer<Pmr_Buffer_Tag, std::pmr::vector<int>>
|
||||
> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {};
|
||||
struct Private : Prev_Private {};
|
||||
};
|
||||
using Pmr_Object_2 = aethera::Attach_Object<Pmr_Object>;
|
||||
struct Counting_Resource : std::pmr::memory_resource {
|
||||
std::pmr::memory_resource* upstream{std::pmr::new_delete_resource()};
|
||||
std::size_t allocations{};
|
||||
private:
|
||||
void* do_allocate(std::size_t bytes, std::size_t alignment) override {
|
||||
++allocations;
|
||||
return upstream->allocate(bytes, alignment);
|
||||
}
|
||||
void do_deallocate(void* pointer, std::size_t bytes, std::size_t alignment) override {
|
||||
upstream->deallocate(pointer, bytes, alignment);
|
||||
}
|
||||
bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
|
||||
return this == &other;
|
||||
}
|
||||
};
|
||||
static_assert(aethera::Object<SEP>);
|
||||
static_assert(aethera::Object<SEP_Child>);
|
||||
static_assert(aethera::Object<Data_Object>);
|
||||
static_assert(aethera::Object<aethera::_2D::Renderable>);
|
||||
static_assert(aethera::Object<aethera::_2D::Secene>);
|
||||
static_assert(aethera::Object<Test_Renderable>);
|
||||
static_assert(aethera::Object<Pmr_Object>);
|
||||
int main() {
|
||||
using Secene_Object = aethera::Attach_Object<aethera::_2D::Secene>;
|
||||
using Prepare_Tag = aethera::Prepare_Data_Tag;
|
||||
using Layout_Tag = aethera::_2D::Layout_Tag;
|
||||
using Extra_Tag = aethera::_2D::Extra_Tag;
|
||||
std::vector<int> order;
|
||||
std::vector<int> direct_order;
|
||||
Data_Object_2 direct_data(9, &direct_order);
|
||||
direct_data.prepare_data();
|
||||
direct_data.exchange_object();
|
||||
if (direct_order != std::vector<int>({9})) return 112;
|
||||
if (direct_data.render_buffer<Data_Cache>().value != 9) return 113;
|
||||
Test_Renderable_2 direct_renderable(8, &direct_order);
|
||||
direct_renderable.paint();
|
||||
if (direct_order != std::vector<int>({9, 80})) return 114;
|
||||
Secene_Object direct_scene;
|
||||
direct_scene.execute_prepare();
|
||||
direct_scene.execute_paint();
|
||||
Counting_Resource buffer_resource;
|
||||
Pmr_Object_2::Builder pmr_buffer_builder;
|
||||
pmr_buffer_builder.set_pmr(buffer_resource);
|
||||
std::pmr::vector<int> pmr_initial;
|
||||
pmr_initial.assign({1, 2, 3});
|
||||
pmr_buffer_builder.set<Pmr_Buffer_Tag>(pmr_initial);
|
||||
auto pmr_buffer_object = pmr_buffer_builder.build();
|
||||
if (pmr_buffer_object->pmr().resource() != &buffer_resource) return 108;
|
||||
if (pmr_buffer_object->render_buffer<Pmr_Buffer_Tag>().get_allocator().resource() != pmr_buffer_object->memory_resource()) return 109;
|
||||
pmr_buffer_object->buffer<Pmr_Buffer_Tag>().push_back(4);
|
||||
if (buffer_resource.allocations == 0) return 110;
|
||||
Counting_Resource scene_resource;
|
||||
// Buffer Builder initialization writes cache/render together.
|
||||
Data_Object_2::Builder data_builder(4, &order);
|
||||
data_builder.set<Data_Cache>(Data_Cache{40});
|
||||
auto data_object = data_builder.build();
|
||||
Test_Renderable_2::Builder c_builder(3, &order);
|
||||
c_builder.set<Test_Cache>(Test_Cache{30});
|
||||
c_builder.set<Test_Cache_Tag>(Test_Cache{300});
|
||||
auto c_object = c_builder.build();
|
||||
Test_Renderable_2::Builder b_builder(2, &order);
|
||||
b_builder.set<Test_Cache>(Test_Cache{20});
|
||||
auto b_object = b_builder.build();
|
||||
Test_Renderable_2::Builder a_builder(1, &order);
|
||||
a_builder.set<Test_Cache>(Test_Cache{10});
|
||||
auto a_object = a_builder.build();
|
||||
if (data_object->render_buffer<Data_Cache>().value != 40) return 1;
|
||||
if (c_object->render_buffer<Test_Cache>().value != 30) return 2;
|
||||
if (c_object->render_buffer<Test_Cache_Tag>().value != 300) return 3;
|
||||
if (b_object->render_buffer<Test_Cache>().value != 20) return 4;
|
||||
if (a_object->render_buffer<Test_Cache>().value != 10) return 5;
|
||||
// Builder initial rely editing is direct; check() validates all rely graphs together.
|
||||
Secene_Object::Builder scene;
|
||||
scene.set_pmr(scene_resource);
|
||||
scene.add_renderable({a_object.get(), b_object.get(), c_object.get()});
|
||||
scene.add_rely<
|
||||
Prepare_Tag,
|
||||
&Data_Object::State::value
|
||||
>(c_object.get(), data_object.get());
|
||||
scene.add_rely<
|
||||
Prepare_Tag,
|
||||
&Test_Renderable::State::value
|
||||
>(b_object.get(), c_object.get());
|
||||
scene.add_rely<
|
||||
Prepare_Tag,
|
||||
Test_Cache
|
||||
>(a_object.get(), b_object.get());
|
||||
scene.add_rely<
|
||||
Prepare_Tag,
|
||||
Test_Cache_Tag
|
||||
>(a_object.get(), c_object.get());
|
||||
scene.edit_rely<Layout_Tag, Extra_Tag>(
|
||||
[&](auto& layout, auto& extra) {
|
||||
layout.template add_rely<&Data_Object::State::other>(
|
||||
c_object.get(),
|
||||
data_object.get()
|
||||
);
|
||||
extra.template add_dirty_rely<Layout_Tag>(
|
||||
b_object.get(),
|
||||
c_object.get()
|
||||
);
|
||||
}
|
||||
);
|
||||
scene.add_rely<
|
||||
Prepare_Tag,
|
||||
Test_Cache
|
||||
>(c_object.get(), a_object.get());
|
||||
auto invalid_initial = scene.check();
|
||||
if (invalid_initial) return 6;
|
||||
if (invalid_initial.error() != aethera::Rely_Error::cycle) return 7;
|
||||
scene.remove_rely<
|
||||
Prepare_Tag,
|
||||
Test_Cache
|
||||
>(c_object.get(), a_object.get());
|
||||
auto initial_check = scene.check();
|
||||
if (!initial_check) return 8;
|
||||
auto scene_object = scene.build();
|
||||
if (scene_object->pmr().resource() != &scene_resource) return 103;
|
||||
if (scene_object->render_rely<Prepare_Tag>().memory_resource() != scene_object->memory_resource()) return 104;
|
||||
if (scene_resource.allocations == 0) return 105;
|
||||
// Rely keeps node/edge introspection and DAG order in the same graph.
|
||||
const auto& prepare_graph = scene_object->render_rely<Prepare_Tag>();
|
||||
if (prepare_graph.size() != 4) return 9;
|
||||
if (!prepare_graph.contains(data_object.get())) return 10;
|
||||
if (!prepare_graph.contains_rely(c_object.get(), data_object.get())) return 11;
|
||||
if (!prepare_graph.contains_rely(b_object.get(), c_object.get())) return 12;
|
||||
if (!prepare_graph.contains_rely(a_object.get(), b_object.get())) return 13;
|
||||
if (prepare_graph.find_edges(a_object.get(), c_object.get()).size() != 1) return 14;
|
||||
auto initial_dag = prepare_graph.dag();
|
||||
if (!initial_dag) return 15;
|
||||
if (initial_dag->size() != 4) return 16;
|
||||
if ((*initial_dag)[0]->object != data_object.get()) return 17;
|
||||
if ((*initial_dag)[1]->object != c_object.get()) return 18;
|
||||
if ((*initial_dag)[2]->object != b_object.get()) return 19;
|
||||
if ((*initial_dag)[3]->object != a_object.get()) return 20;
|
||||
std::vector<aethera::Root*> view_order;
|
||||
bool view_query_ok{true};
|
||||
auto view_walk = scene_object->for_each_rely_dag<Prepare_Tag>(
|
||||
[&](const aethera::Rely::View& view, const aethera::Rely::Node& node) {
|
||||
view_order.push_back(node.object);
|
||||
if (node.object == c_object.get()) {
|
||||
auto dependencies = view.get_rely(node.object);
|
||||
view_query_ok = view_query_ok &&
|
||||
dependencies.size() == 1 &&
|
||||
dependencies[0]->object == data_object.get();
|
||||
}
|
||||
if (node.object == a_object.get()) {
|
||||
auto dependencies = view.get_rely(node.object);
|
||||
bool has_b{};
|
||||
bool has_c{};
|
||||
for (const auto* dependency : dependencies) {
|
||||
has_b = has_b || dependency->object == b_object.get();
|
||||
has_c = has_c || dependency->object == c_object.get();
|
||||
}
|
||||
auto dependents = view.find_rely_to(c_object.get());
|
||||
bool has_b_dependent{};
|
||||
bool has_a_dependent{};
|
||||
for (const auto* dependent : dependents) {
|
||||
has_b_dependent = has_b_dependent || dependent->object == b_object.get();
|
||||
has_a_dependent = has_a_dependent || dependent->object == a_object.get();
|
||||
}
|
||||
view_query_ok = view_query_ok &&
|
||||
dependencies.size() == 2 &&
|
||||
has_b &&
|
||||
has_c &&
|
||||
dependents.size() == 2 &&
|
||||
has_b_dependent &&
|
||||
has_a_dependent &&
|
||||
view.find_edges(a_object.get(), c_object.get()).size() == 1;
|
||||
}
|
||||
}
|
||||
);
|
||||
if (!view_walk) return 100;
|
||||
if (!view_query_ok) return 101;
|
||||
if (view_order != std::vector<aethera::Root*>({
|
||||
data_object.get(),
|
||||
c_object.get(),
|
||||
b_object.get(),
|
||||
a_object.get()
|
||||
}))
|
||||
return 102;
|
||||
if (!data_object->need_prepare_data()) return 21;
|
||||
if (!c_object->need_prepare_data()) return 22;
|
||||
if (!b_object->need_prepare_data()) return 23;
|
||||
if (!a_object->need_prepare_data()) return 24;
|
||||
// Initial prepare follows DAG topology; paint only visits Renderable nodes.
|
||||
scene_object->execute_prepare();
|
||||
if (order != std::vector<int>({4, 3, 2, 1})) return 25;
|
||||
if (data_object->need_prepare_data()) return 26;
|
||||
if (c_object->need_prepare_data()) return 27;
|
||||
if (b_object->need_prepare_data()) return 28;
|
||||
if (a_object->need_prepare_data()) return 29;
|
||||
order.clear();
|
||||
scene_object->execute_paint();
|
||||
if (order != std::vector<int>({30, 20, 10})) return 30;
|
||||
order.clear();
|
||||
// State member dirty -> prepare dirty -> Buffer dirty can continue infecting downstream nodes.
|
||||
c_object->update_state<&Test_Renderable::State::value>(1);
|
||||
if (!c_object->need_prepare_data()) return 31;
|
||||
if (!b_object->need_prepare_data()) return 32;
|
||||
if (a_object->need_prepare_data()) return 33;
|
||||
scene_object->execute_prepare();
|
||||
if (order != std::vector<int>({3, 2, 1})) return 34;
|
||||
order.clear();
|
||||
scene_object->execute_paint();
|
||||
if (order != std::vector<int>({30, 20, 10})) return 35;
|
||||
order.clear();
|
||||
// Explicit Buffer Tag is an independent dirty source from the one-parameter Tagged_Buffer Tag.
|
||||
c_object->buffer<Test_Cache_Tag>().value = 301;
|
||||
if (!a_object->need_prepare_data()) return 36;
|
||||
if (b_object->need_prepare_data()) return 37;
|
||||
if (c_object->need_prepare_data()) return 38;
|
||||
if (c_object->render_buffer<Test_Cache_Tag>().value != 300) return 39;
|
||||
scene_object->execute_prepare();
|
||||
if (order != std::vector<int>({1})) return 40;
|
||||
order.clear();
|
||||
scene_object->execute_paint();
|
||||
if (order != std::vector<int>({10})) return 41;
|
||||
order.clear();
|
||||
// State source can target a custom dirty Tag, which can itself be another rely source.
|
||||
data_object->update_state<&Data_Object::State::other>(1);
|
||||
if (data_object->need_prepare_data()) return 42;
|
||||
if (!c_object->dirty<Layout_Tag>()) return 43;
|
||||
if (!b_object->dirty<Extra_Tag>()) return 44;
|
||||
if (a_object->dirty<Extra_Tag>()) return 45;
|
||||
if (!c_object->take_dirty<Layout_Tag>()) return 46;
|
||||
if (!b_object->take_dirty<Extra_Tag>()) return 47;
|
||||
// Runtime multi-rely edit is transactional: any invalid graph rolls every edited cache graph back.
|
||||
auto invalid_runtime = scene_object->edit_rely<Prepare_Tag, Extra_Tag>(
|
||||
[&](auto& prepare, auto& extra) {
|
||||
prepare.template add_rely<Test_Cache>(
|
||||
c_object.get(),
|
||||
a_object.get()
|
||||
);
|
||||
extra.template add_rely<&Data_Object::State::other>(
|
||||
a_object.get(),
|
||||
data_object.get()
|
||||
);
|
||||
}
|
||||
);
|
||||
if (invalid_runtime) return 48;
|
||||
if (invalid_runtime.error() != aethera::Rely_Error::cycle) return 49;
|
||||
if (scene_object->rely<Prepare_Tag>().contains_rely(c_object.get(), a_object.get())) return 50;
|
||||
if (scene_object->rely<Extra_Tag>().contains_rely(a_object.get(), data_object.get())) return 51;
|
||||
// A valid multi-rely transaction commits all edited cache graphs together.
|
||||
bool editor_view_ok{};
|
||||
auto valid_runtime = scene_object->edit_rely<Prepare_Tag, Extra_Tag>(
|
||||
[&](auto& prepare, auto& extra) {
|
||||
editor_view_ok =
|
||||
prepare.contains(a_object.get()) &&
|
||||
prepare.contains_rely(a_object.get(), b_object.get()) &&
|
||||
prepare.find_edges(a_object.get(), b_object.get()).size() == 1;
|
||||
prepare.template remove_rely<Test_Cache>(
|
||||
a_object.get(),
|
||||
b_object.get()
|
||||
);
|
||||
extra.template add_rely<&Data_Object::State::other>(
|
||||
a_object.get(),
|
||||
data_object.get()
|
||||
);
|
||||
}
|
||||
);
|
||||
if (!editor_view_ok) return 52;
|
||||
if (!valid_runtime) return 53;
|
||||
if (scene_object->rely<Prepare_Tag>().contains_rely(a_object.get(), b_object.get())) return 54;
|
||||
if (!scene_object->rely<Extra_Tag>().contains_rely(a_object.get(), data_object.get())) return 55;
|
||||
if (!scene_object->render_rely<Prepare_Tag>().contains_rely(a_object.get(), b_object.get())) return 56;
|
||||
if (scene_object->render_rely<Extra_Tag>().contains_rely(a_object.get(), data_object.get())) return 57;
|
||||
// Cache graph edits do not affect active render graphs before exchange().
|
||||
data_object->update_state<&Data_Object::State::other>(2);
|
||||
if (!c_object->dirty<Layout_Tag>()) return 58;
|
||||
if (!b_object->dirty<Extra_Tag>()) return 59;
|
||||
if (a_object->dirty<Extra_Tag>()) return 60;
|
||||
c_object->take_dirty<Layout_Tag>();
|
||||
b_object->take_dirty<Extra_Tag>();
|
||||
scene_object->exchange();
|
||||
if (c_object->render_buffer<Test_Cache_Tag>().value != 301) return 61;
|
||||
if (scene_object->render_rely<Prepare_Tag>().contains_rely(a_object.get(), b_object.get())) return 62;
|
||||
if (!scene_object->render_rely<Extra_Tag>().contains_rely(a_object.get(), data_object.get())) return 63;
|
||||
// After exchange(), the new render graph becomes the active dirty propagation graph.
|
||||
data_object->update_state<&Data_Object::State::other>(3);
|
||||
if (!c_object->dirty<Layout_Tag>()) return 64;
|
||||
if (!b_object->dirty<Extra_Tag>()) return 65;
|
||||
if (!a_object->dirty<Extra_Tag>()) return 66;
|
||||
c_object->take_dirty<Layout_Tag>();
|
||||
b_object->take_dirty<Extra_Tag>();
|
||||
a_object->take_dirty<Extra_Tag>();
|
||||
// Removed one-parameter Tagged_Buffer edge no longer propagates, while another Buffer Tag edge remains active.
|
||||
b_object->buffer<Test_Cache>().value = 222;
|
||||
if (a_object->need_prepare_data()) return 67;
|
||||
c_object->buffer<Test_Cache_Tag>().value = 302;
|
||||
if (!a_object->need_prepare_data()) return 68;
|
||||
scene_object->execute_prepare();
|
||||
if (order != std::vector<int>({1})) return 69;
|
||||
order.clear();
|
||||
scene_object->execute_paint();
|
||||
if (order != std::vector<int>({10})) return 70;
|
||||
order.clear();
|
||||
// Inherited Prop/State/Private hooks and one-parameter Tagged_Buffer still compose through Impl layers.
|
||||
SEP_Child_2::Builder sep_builder;
|
||||
sep_builder.set(&SEP_Child::Prop::value, 123);
|
||||
sep_builder.set(&SEP_Child::Prop::child_value, 456);
|
||||
sep_builder.set<std::vector<int>>(std::vector<int>{1, 2, 3});
|
||||
auto sep = sep_builder.build();
|
||||
if (sep->d.render->value != 0) return 71;
|
||||
if (sep->d.render->child_value != 0) return 72;
|
||||
if (sep->render_buffer<std::vector<int>>() != std::vector<int>({1, 2, 3})) return 73;
|
||||
sep->update_state<&SEP_Child::State::a>(1);
|
||||
sep->update_state<&SEP_Child::State::b>(2);
|
||||
sep->exchange(
|
||||
[](const SEP_Child::State& state) {
|
||||
if (state.a != 2) std::terminate();
|
||||
if (state.b != 12) std::terminate();
|
||||
}
|
||||
);
|
||||
if (sep->d.render->value != 123) return 74;
|
||||
if (sep->d.render->child_value != 456) return 75;
|
||||
Counting_Resource global_resource;
|
||||
auto* old_default = aethera::Pmr::set_default_resource(global_resource);
|
||||
{
|
||||
Pmr_Object_2::Builder global_builder;
|
||||
std::pmr::vector<int> global_initial;
|
||||
global_initial.assign({5, 6, 7});
|
||||
global_builder.set<Pmr_Buffer_Tag>(global_initial);
|
||||
auto global_object = global_builder.build();
|
||||
if (global_object->pmr().resource() != &global_resource) return 106;
|
||||
if (global_object->render_buffer<Pmr_Buffer_Tag>().get_allocator().resource() != global_object->memory_resource()) return 107;
|
||||
if (global_resource.allocations == 0) return 111;
|
||||
}
|
||||
aethera::Pmr::set_default_resource(old_default);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user