首次提交 基础架构完成
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
/.idea
|
||||
/cmake-build*
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(Aethera VERSION 1.0.0 LANGUAGES CXX)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/third_party/build_infra/start.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/kernel/main.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/third_party/build_infra/end.cmake")
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 4,
|
||||
"include": [
|
||||
"toolchain/vs2022.json",
|
||||
"toolchain/vs2019.json"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
include_guard(GLOBAL)
|
||||
block()
|
||||
rcl_init(render_kernel)
|
||||
set(oneTBB_option ${base_options})
|
||||
list(APPEND oneTBB_option
|
||||
-DBUILD_SHARED_LIBS=ON
|
||||
-DTBB_BUILD=ON
|
||||
-DTBB_INSTALL=ON
|
||||
-DTBB_TEST=OFF
|
||||
-DTBB_EXAMPLES=OFF
|
||||
-DTBB_STRICT=OFF
|
||||
-DTBB4PY_BUILD=OFF
|
||||
-DTBBMALLOC_BUILD=OFF
|
||||
-DTBBMALLOC_PROXY_BUILD=OFF
|
||||
)
|
||||
_register_git_cmake_library(render_kernel::oneTBB
|
||||
"https://github.com/uxlfoundation/oneTBB.git"
|
||||
"v2023.1.0"
|
||||
)
|
||||
rcl_get_effective_install_dir(render_kernel::oneTBB ONETBB_ROOT)
|
||||
set(TBB_CONFIG_DIR "${ONETBB_ROOT}/lib/cmake/TBB")
|
||||
rcl_cmake_library_set_cmake_options(render_kernel::oneTBB ${oneTBB_option})
|
||||
rcl_cmake_library_set_other_use_opt(render_kernel::oneTBB
|
||||
"-DTBB_DIR=\"${TBB_CONFIG_DIR}\""
|
||||
)
|
||||
rcl_cmake_library_set_init_script(render_kernel::oneTBB
|
||||
"set(ONETBB_ROOT \"${ONETBB_ROOT}\")"
|
||||
"set(TBB_DIR \"${TBB_CONFIG_DIR}\")"
|
||||
)
|
||||
rcl_cmake_library_set_clear_script(render_kernel::oneTBB
|
||||
"unset(ONETBB_ROOT)"
|
||||
"unset(TBB_DIR)"
|
||||
)
|
||||
endblock()
|
||||
@@ -0,0 +1,96 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/rely.cmake)
|
||||
set(Aethera_Kernel_dependencies render_kernel::oneTBB global::magic_enum global::expected)
|
||||
if (Aethera_BUILD_TESTS)
|
||||
set(Aethera_Kernel_test_targets)
|
||||
list(APPEND Aethera_Kernel_dependencies global::GTest)
|
||||
endif ()
|
||||
rcl_add_dependency_action_targets(Aethera_Kernel_env ${Aethera_Kernel_dependencies})
|
||||
set_target_properties(Aethera_Kernel_env PROPERTIES FOLDER Aethera_Kernel)
|
||||
library_get_missing_with_rely(Aethera_Kernel_dependencies_installed ${Aethera_Kernel_dependencies})
|
||||
if (Aethera_Kernel_dependencies_installed)
|
||||
rcl_log_append(${CMAKE_CURRENT_LIST_LINE} "[FATAL_ERROR] Aethera_Kernel dependencies are not installed. Build Aethera_Kernel_env first")
|
||||
return()
|
||||
endif ()
|
||||
rcl_load_dependency_environment(${Aethera_Kernel_dependencies})
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(TBB CONFIG REQUIRED)
|
||||
find_package(magic_enum CONFIG REQUIRED)
|
||||
find_package(tl-expected CONFIG REQUIRED)
|
||||
if (Aethera_BUILD_TESTS)
|
||||
find_package(GTest CONFIG REQUIRED)
|
||||
endif ()
|
||||
set(Aethera_Kernel_source_dir "${CMAKE_CURRENT_LIST_DIR}/src/kernel")
|
||||
append_glob_source(Aethera_Kernel_sources "${Aethera_Kernel_source_dir}")
|
||||
add_library(Aethera_Kernel STATIC ${Aethera_Kernel_sources})
|
||||
add_library(Renderive::Kernel ALIAS Aethera_Kernel)
|
||||
set_target_properties(Aethera_Kernel PROPERTIES EXPORT_NAME Kernel)
|
||||
target_include_directories(Aethera_Kernel PUBLIC
|
||||
"$<BUILD_INTERFACE:${Aethera_Kernel_source_dir}>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
target_compile_features(Aethera_Kernel PUBLIC cxx_std_20)
|
||||
target_link_libraries(Aethera_Kernel PUBLIC
|
||||
TBB::tbb
|
||||
magic_enum::magic_enum
|
||||
tl::expected)
|
||||
target_link_libraries(Aethera_Kernel PRIVATE Threads::Threads)
|
||||
function(Aethera_stage_kernel_runtime target)
|
||||
if (WIN32)
|
||||
add_custom_command(TARGET "${target}" POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
"$<TARGET_FILE:TBB::tbb>"
|
||||
"$<TARGET_FILE_DIR:${target}>"
|
||||
COMMENT "Staging oneTBB runtime for ${target}"
|
||||
VERBATIM)
|
||||
endif ()
|
||||
endfunction()
|
||||
if (MSVC)
|
||||
target_compile_options(Aethera_Kernel PRIVATE /utf-8)
|
||||
endif ()
|
||||
add_executable(Aethera_Kernel_exe "${CMAKE_CURRENT_LIST_DIR}/src/test/main.cpp")
|
||||
target_link_libraries(Aethera_Kernel_exe PRIVATE Aethera_Kernel)
|
||||
install(TARGETS Aethera_Kernel
|
||||
EXPORT RenderiveTargets
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(DIRECTORY "${Aethera_Kernel_source_dir}/renderive"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" PATTERN "*.inl")
|
||||
if (Aethera_BUILD_TESTS)
|
||||
set(Aethera_Kernel_test_dir "${CMAKE_CURRENT_LIST_DIR}/tests")
|
||||
append_glob_source(Aethera_Kernel_test_sources "${Aethera_Kernel_test_dir}")
|
||||
# Concrete 2D/3D scene tests belong to their owning modules. The former
|
||||
# header-only Scene_Context fixtures were removed with the Builder-based
|
||||
# scene API, so Kernel's suite must remain independently linkable.
|
||||
list(FILTER Aethera_Kernel_test_sources EXCLUDE REGEX
|
||||
"(Real_Time_Data_Test|Renderable_Base_Test|Color_Cache_Test|Renderable_Test|Scene_Base_Test|Scene_Test|Dynamic_Renderable_Lifecycle_Test|Render_Plan_Execution_Test|Scene_Inheritance_Test|Scene_Memory_Resource_Test|Scene_State_Observer_Test|Scene2D_Context_Test|Scene2D_Render_Order_Test|Scene3D_Context_Test|Threading_Contract_Test)\\.cpp$")
|
||||
foreach (Aethera_Kernel_test_source IN LISTS Aethera_Kernel_test_sources)
|
||||
if (NOT Aethera_Kernel_test_source MATCHES "\\.(c|cc|cpp|cxx)$")
|
||||
continue()
|
||||
endif ()
|
||||
file(RELATIVE_PATH Aethera_Kernel_test_name "${Aethera_Kernel_test_dir}" "${Aethera_Kernel_test_source}")
|
||||
string(REGEX REPLACE "\\.[^.]+$" "" Aethera_Kernel_test_name "${Aethera_Kernel_test_name}")
|
||||
string(MAKE_C_IDENTIFIER "${Aethera_Kernel_test_name}" Aethera_Kernel_test_name)
|
||||
set(Aethera_Kernel_test_target "Aethera_Kernel_${Aethera_Kernel_test_name}")
|
||||
add_executable("${Aethera_Kernel_test_target}" "${Aethera_Kernel_test_source}")
|
||||
target_include_directories("${Aethera_Kernel_test_target}" PRIVATE
|
||||
"${Aethera_Kernel_test_dir}"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../render_2D"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../render_3D"
|
||||
)
|
||||
target_link_libraries("${Aethera_Kernel_test_target}" PRIVATE Aethera_Kernel GTest::gtest_main)
|
||||
Aethera_stage_kernel_runtime("${Aethera_Kernel_test_target}")
|
||||
add_test(NAME "${Aethera_Kernel_test_target}" COMMAND "${Aethera_Kernel_test_target}")
|
||||
set_tests_properties("${Aethera_Kernel_test_target}" PROPERTIES
|
||||
LABELS "Aethera_Kernel"
|
||||
ENVIRONMENT "Aethera_ERROR_MODE=exception")
|
||||
list(APPEND Aethera_Kernel_test_targets "${Aethera_Kernel_test_target}")
|
||||
endforeach ()
|
||||
add_custom_target(Aethera_Kernel_check
|
||||
COMMAND "${CMAKE_CTEST_COMMAND}" --test-dir "${CMAKE_BINARY_DIR}"
|
||||
-C "$<CONFIG>" -L "^Aethera_Kernel$" --output-on-failure
|
||||
DEPENDS ${Aethera_Kernel_test_targets}
|
||||
USES_TERMINAL)
|
||||
endif ()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,207 @@
|
||||
#include "base.hpp"
|
||||
namespace aethera::_2D {
|
||||
struct Color_Cache {};
|
||||
struct Renderable : Impl<Renderable, Root, Tagged_Buffer<Color_Cache>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
private:
|
||||
using Call = void (*)(Renderable*);
|
||||
Call paint_call{};
|
||||
bool paint_dirty{true};
|
||||
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);
|
||||
};
|
||||
}
|
||||
public:
|
||||
void mark_paint_dirty() {
|
||||
paint_dirty = true;
|
||||
}
|
||||
bool take_paint_dirty() {
|
||||
if (!paint_dirty) return false;
|
||||
paint_dirty = false;
|
||||
return true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
public:
|
||||
Secene() : renderables(memory_resource()),
|
||||
renderable_nodes(memory_resource()) {}
|
||||
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;
|
||||
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);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
std::unique_ptr<Object> build() {
|
||||
this->object->bind_renderables();
|
||||
return std::move(this->object);
|
||||
}
|
||||
};
|
||||
struct Private : Prev_Private {
|
||||
template <typename Object>
|
||||
void after_exchange(
|
||||
Object* object,
|
||||
typename Object::Prop*,
|
||||
typename Object::State*,
|
||||
const typename Object::Prop*,
|
||||
const typename Object::State*) {
|
||||
std::pmr::unordered_set<Root*> exchanged{object->memory_resource()};
|
||||
object->for_each_render_rely(
|
||||
[&](const Rely& rely) {
|
||||
rely.for_each(
|
||||
[&](const auto& node) {
|
||||
if (exchanged.insert(node.object).second) node.object->exchange_object();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,390 @@
|
||||
#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;
|
||||
}
|
||||
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$root"
|
||||
origin="$(git remote get-url origin)"
|
||||
origin="${origin%/}"
|
||||
remote_root="$(printf '%s\n' "$origin" | sed -E 's#/[^/]+(\.git)?$##')"
|
||||
remote_root="${remote_root%/*}"
|
||||
echo "remote_root: $remote_root"
|
||||
get_default_branch() {
|
||||
local repo="$1"
|
||||
local branch
|
||||
branch="$(git ls-remote --symref "$repo" HEAD | awk '$1 == "ref:" {
|
||||
sub("^refs/heads/", "", $2)
|
||||
print $2
|
||||
exit
|
||||
}')"
|
||||
if [ -z "$branch" ]; then
|
||||
printf '无法获取远程仓库默认分支: %s\n' "$repo" >&2
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "$branch"
|
||||
}
|
||||
fetch_repo() {
|
||||
local repo="$1"
|
||||
local dir="$2"
|
||||
local branch="${3-}"
|
||||
if [ -z "$branch" ]; then
|
||||
branch="$(get_default_branch "$repo")"
|
||||
fi
|
||||
if [ -d "$dir" ]; then
|
||||
git -C "$dir" rev-parse --is-inside-work-tree >/dev/null
|
||||
git -C "$dir" remote set-url origin "$repo"
|
||||
git -C "$dir" fetch origin "$branch"
|
||||
git -C "$dir" checkout "$branch"
|
||||
git -C "$dir" pull --ff-only origin "$branch"
|
||||
else
|
||||
git clone --branch "$branch" --single-branch "$repo" "$dir"
|
||||
fi
|
||||
}
|
||||
fetch_repo_ex() {
|
||||
local name="$1"
|
||||
local branch="${2-}"
|
||||
local repo="${remote_root}/${name}"
|
||||
local dir="${name##*/}"
|
||||
fetch_repo "$repo" "third_party/${dir}" "$branch"
|
||||
}
|
||||
fetch_repo_ex "ae/build_infra"
|
||||
@@ -0,0 +1,40 @@
|
||||
include_guard(GLOBAL)
|
||||
#set(base_dir "C:/Program Files/Microsoft Visual Studio/18/Enterprise/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64")
|
||||
#set(CMAKE_C_COMPILER "${base_dir}/cl.bat" CACHE FILEPATH "")
|
||||
#set(CMAKE_CXX_COMPILER "{${base_dir}}/cl.bat" CACHE FILEPATH "")
|
||||
#set(CMAKE_MAKE_PROGRAM "C:/Program Files/Microsoft Visual Studio/18/Enterprise/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe" CACHE FILEPATH "")
|
||||
function(_append_msvc_init_options variable)
|
||||
set(value "${${variable}}")
|
||||
separate_arguments(existing_options WINDOWS_COMMAND "${value}")
|
||||
foreach (option IN LISTS ARGN)
|
||||
list(FIND existing_options "${option}" option_index)
|
||||
if (option_index EQUAL -1)
|
||||
list(APPEND existing_options "${option}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
list(JOIN existing_options " " value)
|
||||
set(${variable} "${value}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
_append_msvc_init_options(
|
||||
CMAKE_CXX_FLAGS_INIT
|
||||
/wd4819
|
||||
/utf-8
|
||||
/Zc:preprocessor
|
||||
/DNOMINMAX
|
||||
/D_USE_MATH_DEFINES
|
||||
/EHsc
|
||||
/bigobj
|
||||
/DWIN32_LEAN_AND_MEAN
|
||||
)
|
||||
_append_msvc_init_options(
|
||||
CMAKE_EXE_LINKER_FLAGS_RELEASE_INIT
|
||||
/PROFILE
|
||||
)
|
||||
_append_msvc_init_options(
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELEASE_INIT
|
||||
/PROFILE
|
||||
)
|
||||
_append_msvc_init_options(
|
||||
CMAKE_MODULE_LINKER_FLAGS_RELEASE_INIT
|
||||
/PROFILE
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"version": 10,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 21,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "vs2019",
|
||||
"hidden": true,
|
||||
"graphviz": "${sourceDir}/../build/${presetName}/graphviz/yc.dot",
|
||||
"generator": "Ninja",
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/toolchain/vs2019.cmake"
|
||||
},
|
||||
"vendor": {
|
||||
"jetbrains.com/clion": {
|
||||
"toolchain": "vs2019"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vs2019_Debug",
|
||||
"inherits": "vs2019",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vs2019_Release",
|
||||
"inherits": "vs2019",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
set(dir "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64")
|
||||
|
||||
set(CMAKE_CXX_COMPILER "${dir}/cl.bat")
|
||||
set(CMAKE_C_COMPILER "${dir}/cl.bat")
|
||||
set(CMAKE_LINKER "${dir}/link.bat")
|
||||
|
||||
#set(CMAKE_CXX_COMPILER "${dir}/cl.exe")
|
||||
#set(CMAKE_C_COMPILER "${dir}/cl.exe")
|
||||
#set(CMAKE_LINKER "${dir}/link.exe")
|
||||
|
||||
# [2/2] C:\Windows\system32\cmd.exe /C "cd . && "C:\Program Files\JetBrains\CLion 2025.3.4\bin\cmake\win\x64\bin\cmake.exe" -E vs_link_exe --msvc-ver=1944 --intdir=CMakeFiles\radio.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100261~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100261~1.0\x64\mt.exe --manifests -- C:\PROGRA~1\MICROS~1\2022\ENTERP~1\VC\Tools\MSVC\1444~1.352\bin\Hostx64\x64\LINK_2~1.BAT /nologo @CMakeFiles\radio.rsp /out:radio.exe /implib:radio.lib /pdb:radio.pdb /version:0.0 /machine:x64 /debug /INCREMENTAL /subsystem:console && cd ."
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"version": 10,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 21,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "vs2022",
|
||||
"hidden": true,
|
||||
"graphviz": "${sourceDir}/../build/${presetName}/graphviz/yc.dot",
|
||||
"generator": "Ninja",
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/toolchain/vs2022.cmake"
|
||||
},
|
||||
"vendor": {
|
||||
"jetbrains.com/clion": {
|
||||
"toolchain": "vs2022"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vs2022_debug",
|
||||
"inherits": "vs2022",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vs2022_release",
|
||||
"inherits": "vs2022",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user