diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index b27684cf5..8662cc697 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -125,8 +125,6 @@ add_library(wmoge STATIC gfx/gfx_driver.hpp gfx/gfx_dynamic_buffers.cpp gfx/gfx_dynamic_buffers.hpp - gfx/gfx_pass.hpp - gfx/gfx_pass.cpp gfx/gfx_pipeline_cache.cpp gfx/gfx_pipeline_cache.hpp gfx/gfx_pipeline.cpp @@ -185,6 +183,20 @@ add_library(wmoge STATIC gfx/vulkan/vk_vert_format.hpp gfx/vulkan/vk_window.cpp gfx/vulkan/vk_window.hpp + glsl/glsl_include_processor.cpp + glsl/glsl_include_processor.hpp + glsl/glsl_shader_compiler.cpp + glsl/glsl_shader_compiler.hpp + grc/grc_shader_class_builder.cpp + grc/grc_shader_class_builder.hpp + grc/grc_shader_class.cpp + grc/grc_shader_class.hpp + grc/grc_shader_manager.cpp + grc/grc_shader_manager.hpp + grc/grc_shader_reflection.cpp + grc/grc_shader_reflection.hpp + grc/grc_shader.cpp + grc/grc_shader.hpp hgfx/hgfx_pass.cpp hgfx/hgfx_pass.hpp hgfx/hgfx_pass_base.cpp diff --git a/engine/core/async.cpp b/engine/core/async.cpp index 7b04c14ee..bb3656109 100644 --- a/engine/core/async.cpp +++ b/engine/core/async.cpp @@ -27,6 +27,7 @@ #include "core/async.hpp" +#include "async.hpp" #include "debug/profiler.hpp" namespace wmoge { @@ -86,4 +87,10 @@ namespace wmoge { return Async(std::move(state)); } + Async Async::completed() { + auto state = make_async_op(); + state->set_result(0); + return Async(state); + } + }// namespace wmoge \ No newline at end of file diff --git a/engine/core/async.hpp b/engine/core/async.hpp index 474161b3b..2623c2dfa 100644 --- a/engine/core/async.hpp +++ b/engine/core/async.hpp @@ -248,6 +248,13 @@ namespace wmoge { * @return New async signaled when all deps finished or failed if one failed */ static Async join(ArrayView dependencies); + + /** + * @brief Make signaled (completed) async op + * + * @return Completed async + */ + static Async completed(); }; /** diff --git a/engine/debug/profiler.hpp b/engine/debug/profiler.hpp index 9589c04ab..aac66ebff 100644 --- a/engine/debug/profiler.hpp +++ b/engine/debug/profiler.hpp @@ -186,4 +186,4 @@ namespace wmoge { #define WG_PROFILE_CAPTURE_END(capture) \ Engine::instance()->profiler()->end_capture(); \ capture->save_to_json(); \ - capture.reset(); \ No newline at end of file + capture.reset(); diff --git a/engine/ecs/ecs_registry.hpp b/engine/ecs/ecs_registry.hpp index f27991b8a..c9b465f3f 100644 --- a/engine/ecs/ecs_registry.hpp +++ b/engine/ecs/ecs_registry.hpp @@ -27,6 +27,7 @@ #pragma once +#include "core/array_view.hpp" #include "core/fast_map.hpp" #include "core/fast_vector.hpp" #include "core/string_id.hpp" @@ -37,6 +38,7 @@ #include "memory/mem_pool.hpp" #include +#include #include namespace wmoge { @@ -52,22 +54,29 @@ namespace wmoge { EcsRegistry(EcsRegistry&&) = delete; ~EcsRegistry() = default; - [[nodiscard]] int get_component_idx(const Strid& name); - [[nodiscard]] const EcsComponentInfo& get_component_info(const Strid& name); - [[nodiscard]] const EcsComponentInfo& get_component_info(int idx); - [[nodiscard]] MemPool& get_component_pool(int idx); - [[nodiscard]] MemPool& get_entity_pool(); - [[nodiscard]] int get_chunk_size() const { return m_chunk_size; } - [[nodiscard]] int get_expand_size() const { return m_expand_size; } + [[nodiscard]] int get_component_idx(const Strid& name); + [[nodiscard]] const EcsComponentInfo& get_component_info(const Strid& name); + [[nodiscard]] const EcsComponentInfo& get_component_info(int idx); + [[nodiscard]] MemPool& get_component_pool(int idx); + [[nodiscard]] MemPool& get_entity_pool(); + [[nodiscard]] int get_chunk_size() const { return m_chunk_size; } + [[nodiscard]] int get_expand_size() const { return m_expand_size; } + [[nodiscard]] ArrayView get_systems() const { return m_systems; } template void register_component(); + template + std::shared_ptr register_system(); + private: - std::array m_components_info; // type info of component, indexed by component id - std::array, EcsLimits::MAX_COMPONENTS> m_components_pool; // pools to allocate components chunks - fast_map m_components_name_to_idx;// resolve component name to its idx - std::unique_ptr m_entity_pool; // pool to allocate entities chunks + static const int MAX_CMP = EcsLimits::MAX_COMPONENTS; + + std::array m_components_info; // type info of component, indexed by component id + std::array, MAX_CMP> m_components_pool; // pools to allocate components chunks + fast_map m_components_name_to_idx;// resolve component name to its idx + std::unique_ptr m_entity_pool; // pool to allocate entities chunks + std::vector m_systems; // registered global systems int m_chunk_size = 16;// num of components of a single type sequentially allocate in one chunk int m_expand_size = 2; // num of chunks in a single pooled allocation in head @@ -108,4 +117,11 @@ namespace wmoge { m_components_pool[component_info.idx] = std::make_unique(component_info.size * m_chunk_size, m_expand_size); } + template + inline std::shared_ptr EcsRegistry::register_system() { + auto sys = std::make_shared(); + m_systems.emplace_back(sys); + return sys; + } + }// namespace wmoge \ No newline at end of file diff --git a/engine/ecs/ecs_system.hpp b/engine/ecs/ecs_system.hpp index bff781ecf..a3e2a2464 100644 --- a/engine/ecs/ecs_system.hpp +++ b/engine/ecs/ecs_system.hpp @@ -52,8 +52,8 @@ namespace wmoge { * @brief How system must be executed */ enum class EcsSystemExecMode { - OnMain, // On main (game) thread only without parallel speed up - OnWorkers// In task manager with multiple parallel tasks + SingleThread,// One thread only without parallel speed up + WorkerThreads// In task manager with multiple parallel tasks }; /** @@ -83,20 +83,25 @@ namespace wmoge { virtual void process_batch(class EcsWorld& world, EcsArchStorage& storage, int start_entity, int count) = 0; [[nodiscard]] virtual EcsSystemType get_type() const { return EcsSystemType::Update; } - [[nodiscard]] virtual EcsSystemExecMode get_exec_mode() const { return EcsSystemExecMode::OnMain; } + [[nodiscard]] virtual EcsSystemExecMode get_exec_mode() const { return EcsSystemExecMode::SingleThread; } [[nodiscard]] virtual Strid get_name() const = 0; [[nodiscard]] virtual EcsQuery get_query() const = 0; }; + /** + * @brief Shader pointer to a ecs system + */ + using EcsSystemPtr = std::shared_ptr; + /** * @brief Holds system information for execution within a world */ struct EcsSystemInfo { - EcsQuery query; // system query, which archetypes its affects - EcsSystemType type; // system type (exec, deletion, etc.) - EcsSystemExecMode exec_mode; // execution mode - std::shared_ptr system; // cached system ptr - std::vector filtered_arch;// pre-filtered arch idx to execute using this system + EcsQuery query; // system query, which archetypes its affects + EcsSystemType type; // system type (exec, deletion, etc.) + EcsSystemExecMode exec_mode; // execution mode + EcsSystemPtr system; // cached system ptr + std::vector filtered_arch;// pre-filtered arch idx to execute using this system }; /** diff --git a/engine/ecs/ecs_world.cpp b/engine/ecs/ecs_world.cpp index e2b063268..62b0d155f 100644 --- a/engine/ecs/ecs_world.cpp +++ b/engine/ecs/ecs_world.cpp @@ -29,6 +29,7 @@ #include "core/log.hpp" #include "core/task.hpp" +#include "core/task_manager.hpp" #include "core/task_parallel_for.hpp" #include "debug/profiler.hpp" #include "ecs/ecs_registry.hpp" @@ -39,6 +40,11 @@ namespace wmoge { EcsWorld::EcsWorld() { m_task_manager = Engine::instance()->task_manager(); + m_ecs_registry = Engine::instance()->ecs_registry(); + + for (const EcsSystemPtr& system : m_ecs_registry->get_systems()) { + register_system(system); + } } EcsWorld::~EcsWorld() { @@ -99,10 +105,8 @@ namespace wmoge { const EcsArch right_arch = m_arch_by_idx[right_info.arch]; const EcsArch left_arch = m_arch_by_idx[left_info.arch]; - EcsRegistry* ecs_registry = Engine::instance()->ecs_registry(); - EcsArch(right_arch & left_arch).for_each_component([&](int idx) { - const EcsComponentInfo& info = ecs_registry->get_component_info(idx); + const EcsComponentInfo& info = m_ecs_registry->get_component_info(idx); info.swap(right_storage->get_component(right_info.storage, idx), left_storage->get_component(left_info.storage, idx)); }); } @@ -233,8 +237,8 @@ namespace wmoge { } } - void EcsWorld::execute_system(const std::shared_ptr& system) { - WG_AUTO_PROFILE_ECS("EcsWorld::execute_system"); + Async EcsWorld::schedule_system(const std::shared_ptr& system, Async depends_on) { + WG_AUTO_PROFILE_ECS("EcsWorld::schedule_system"); assert(system); assert(m_system_to_idx.find(system->get_name()) != m_system_to_idx.end()); @@ -242,79 +246,41 @@ namespace wmoge { EcsSystemInfo& system_info = m_systems[m_system_to_idx[system->get_name()]]; switch (system_info.exec_mode) { - case EcsSystemExecMode::OnMain: { - for (const int arch_idx : system_info.filtered_arch) { - EcsArchStorage& storage = *m_arch_storage[arch_idx]; - const int size = storage.get_size(); - const int start_entity = 0; - const int count = size; - - system_info.system->process_batch(*this, storage, start_entity, count); - } - - break; - } - - case EcsSystemExecMode::OnWorkers: { - TaskParallelFor task(system->get_name(), [&](TaskContext&, int batch_id, int batch_count) { + case EcsSystemExecMode::SingleThread: { + Task task(system_info.system->get_name(), [&](TaskContext&) { for (const int arch_idx : system_info.filtered_arch) { - EcsArchStorage& storage = *m_arch_storage[arch_idx]; - const int size = storage.get_size(); - const auto [start_entity, count] = Math::batch_start_count(size, batch_id, batch_count); + EcsArchStorage& storage = *m_arch_storage[arch_idx]; + const int size = storage.get_size(); + const int start_entity = 0; + const int count = size; system_info.system->process_batch(*this, storage, start_entity, count); } + return 0; }); - task.schedule(m_task_manager->get_num_workers(), 1).wait_completed(); - - break; + return task.schedule(depends_on).as_async(); } - default: - WG_LOG_ERROR("unknown system exec mode"); - } - } - - void EcsWorld::execute_system(EcsSystem& system) { - WG_AUTO_PROFILE_ECS("EcsWorld::execute_system"); - - const std::vector filtered_arch = filter_arch_idx(system.get_query()); - - switch (system.get_exec_mode()) { - case EcsSystemExecMode::OnMain: { - for (const int arch_idx : filtered_arch) { - EcsArchStorage& storage = *m_arch_storage[arch_idx]; - const int size = storage.get_size(); - const int start_entity = 0; - const int count = size; - - system.process_batch(*this, storage, start_entity, count); - } - - break; - } - - case EcsSystemExecMode::OnWorkers: { - TaskParallelFor task(system.get_name(), [&](TaskContext&, int batch_id, int batch_count) { - for (const int arch_idx : filtered_arch) { + case EcsSystemExecMode::WorkerThreads: { + TaskParallelFor task(system->get_name(), [&](TaskContext&, int batch_id, int batch_count) { + for (const int arch_idx : system_info.filtered_arch) { EcsArchStorage& storage = *m_arch_storage[arch_idx]; const int size = storage.get_size(); const auto [start_entity, count] = Math::batch_start_count(size, batch_id, batch_count); - system.process_batch(*this, storage, start_entity, count); + system_info.system->process_batch(*this, storage, start_entity, count); } return 0; }); - task.schedule(m_task_manager->get_num_workers(), 1).wait_completed(); - - break; + return task.schedule(m_task_manager->get_num_workers(), 1, depends_on).as_async(); } default: WG_LOG_ERROR("unknown system exec mode"); + return Async{}; } } diff --git a/engine/ecs/ecs_world.hpp b/engine/ecs/ecs_world.hpp index 10ee88000..aa60351e4 100644 --- a/engine/ecs/ecs_world.hpp +++ b/engine/ecs/ecs_world.hpp @@ -27,10 +27,10 @@ #pragma once +#include "core/async.hpp" #include "core/callback_queue.hpp" #include "core/fast_map.hpp" #include "core/synchronization.hpp" -#include "core/task_manager.hpp" #include "ecs/ecs_component.hpp" #include "ecs/ecs_core.hpp" #include "ecs/ecs_entity.hpp" @@ -112,11 +112,8 @@ namespace wmoge { /** @brief Registers system within a world */ void register_system(const std::shared_ptr& system); - /** @brief Manual trigger of system execution */ - void execute_system(const std::shared_ptr& system); - - /** @brief Manual trigger of system execution */ - void execute_system(EcsSystem& system); + /** @brief Schedules system to be executed */ + Async schedule_system(const std::shared_ptr& system, Async depends_on = Async{}); /** @brief Exec function for each entity matching query*/ void each(const EcsQuery& query, const std::function& func); @@ -150,8 +147,9 @@ namespace wmoge { fast_vector m_attributes;// custom attributes to access context within world - CallbackQueue m_queue; // queue for async world operations, flushed on sync - TaskManager* m_task_manager;// manager for parallel system update + CallbackQueue m_queue; // queue for async world operations, flushed on sync + class TaskManager* m_task_manager;// manager for parallel system update + class EcsRegistry* m_ecs_registry;// registry of the ecs world SpinMutex m_mutex; }; diff --git a/engine/engine.hpp b/engine/engine.hpp index c58f03624..feb8415e7 100644 --- a/engine/engine.hpp +++ b/engine/engine.hpp @@ -98,7 +98,6 @@ #include "gfx/gfx_desc_set.hpp" #include "gfx/gfx_driver.hpp" #include "gfx/gfx_dynamic_buffers.hpp" -#include "gfx/gfx_pass.hpp" #include "gfx/gfx_pipeline.hpp" #include "gfx/gfx_pipeline_cache.hpp" #include "gfx/gfx_render_pass.hpp" @@ -110,6 +109,11 @@ #include "gfx/gfx_vert_format.hpp" #include "gfx/gfx_vert_format_cache.hpp" +#include "grc/grc_shader.hpp" +#include "grc/grc_shader_class.hpp" +#include "grc/grc_shader_class_builder.hpp" +#include "grc/grc_shader_reflection.hpp" + #include "hgfx/hgfx_pass.hpp" #include "hgfx/hgfx_pass_base.hpp" #include "hgfx/hgfx_pass_text.hpp" diff --git a/engine/gfx/gfx_defs.hpp b/engine/gfx/gfx_defs.hpp index 3d0dba177..b79f562f5 100644 --- a/engine/gfx/gfx_defs.hpp +++ b/engine/gfx/gfx_defs.hpp @@ -25,8 +25,7 @@ /* SOFTWARE. */ /**********************************************************************************/ -#ifndef WMOGE_GFX_DEFS_HPP -#define WMOGE_GFX_DEFS_HPP +#pragma once #include "core/data.hpp" #include "core/mask.hpp" @@ -35,6 +34,7 @@ #include "math/vec.hpp" #include +#include #include namespace wmoge { @@ -500,6 +500,4 @@ namespace wmoge { int uniform_block_offset_alignment = -1; }; -}// namespace wmoge - -#endif//WMOGE_GFX_DEFS_HPP +}// namespace wmoge \ No newline at end of file diff --git a/engine/glsl/glsl_include_processor.cpp b/engine/glsl/glsl_include_processor.cpp new file mode 100644 index 000000000..d93b6ce7a --- /dev/null +++ b/engine/glsl/glsl_include_processor.cpp @@ -0,0 +1,81 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#include "glsl_include_processor.hpp" + +namespace wmoge { + + GlslIncludeProcessor::GlslIncludeProcessor(std::string folder, FileSystem* file_system) { + m_folder = std::move(folder); + m_file_system = file_system; + } + + Status GlslIncludeProcessor::parse_file(const Strid& file) { + const std::string file_path = m_folder + '/' + file.str(); + std::string content; + + if (!m_file_system->read_file(file_path, content)) { + WG_LOG_ERROR("failed read shader source " << file_path); + return StatusCode::FailedRead; + } + + std::stringstream stream(content); + std::string line; + + while (std::getline(stream, line)) { + const auto prefix = std::string("#include "); + auto pragma = line.find(prefix); + + if (pragma != std::string::npos) { + auto line_len = line.length(); + auto cut_start = prefix.length() + 1; + auto cut_count = line_len - cut_start - 1; + + const Strid include_file = SID(line.substr(cut_start, cut_count)); + + const auto recursion_check = std::find(m_includes.begin(), m_includes.end(), include_file); + + if (recursion_check != m_includes.end()) { + continue; + } + + if (!parse_file(include_file)) { + WG_LOG_ERROR("failed parse include file " << include_file); + return StatusCode::Error; + } + + m_includes.push_back(include_file); + + } else { + m_result << line; + } + } + + return StatusCode::Ok; + } + +}// namespace wmoge \ No newline at end of file diff --git a/engine/glsl/glsl_include_processor.hpp b/engine/glsl/glsl_include_processor.hpp new file mode 100644 index 000000000..f2654cb81 --- /dev/null +++ b/engine/glsl/glsl_include_processor.hpp @@ -0,0 +1,65 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#pragma once + +#include "core/string_id.hpp" +#include "platform/file_system.hpp" + +#include +#include + +namespace wmoge { + + /** + * @class GlslIncludeProcessor + * @brief Process glsl-like syntax includes and emits final file with includes list + */ + class GlslIncludeProcessor { + public: + GlslIncludeProcessor(std::string folder, FileSystem* file_system); + + /** + * Recursively parse file collecting all includes + * + * @param file Starting file to parse + * + * @result Ok on success + */ + Status parse_file(const Strid& file); + + std::vector& get_includes() { return m_includes; } + std::string get_result() { return m_result.str(); } + + private: + std::vector m_includes; + std::stringstream m_result; + std::string m_folder; + FileSystem* m_file_system; + }; + +}// namespace wmoge \ No newline at end of file diff --git a/engine/glsl/glsl_shader_compiler.cpp b/engine/glsl/glsl_shader_compiler.cpp new file mode 100644 index 000000000..e592c3f1b --- /dev/null +++ b/engine/glsl/glsl_shader_compiler.cpp @@ -0,0 +1,32 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#include "glsl_shader_compiler.hpp" + +namespace wmoge { + +} \ No newline at end of file diff --git a/engine/glsl/glsl_shader_compiler.hpp b/engine/glsl/glsl_shader_compiler.hpp new file mode 100644 index 000000000..de8691d07 --- /dev/null +++ b/engine/glsl/glsl_shader_compiler.hpp @@ -0,0 +1,39 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#pragma once + +namespace wmoge { + + /** + * @class GlslShaderCompiler + * @brief Glslang based compiler for shaders + */ + class GlslShaderCompiler { + }; + +}// namespace wmoge \ No newline at end of file diff --git a/engine/grc/grc_shader.cpp b/engine/grc/grc_shader.cpp new file mode 100644 index 000000000..46f2b3a4c --- /dev/null +++ b/engine/grc/grc_shader.cpp @@ -0,0 +1,32 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#include "grc_shader.hpp" + +namespace wmoge { + +} \ No newline at end of file diff --git a/engine/gfx/gfx_pass.hpp b/engine/grc/grc_shader.hpp similarity index 69% rename from engine/gfx/gfx_pass.hpp rename to engine/grc/grc_shader.hpp index 0ceb52034..ab7cfb57b 100644 --- a/engine/gfx/gfx_pass.hpp +++ b/engine/grc/grc_shader.hpp @@ -27,41 +27,37 @@ #pragma once -#include "gfx/gfx_defs.hpp" +#include "grc/grc_shader_reflection.hpp" namespace wmoge { /** - * @class GfxPassDesc - * @brief Base class for a description to setup a gfx pass - * - * Gfx pass descriptor is a minimal self-contained representation, - * required to setup a GfxPass for rendering. - * - * Description can be created from any system and used to automate - * setup of required for the rendering data: - * - gather required defines for the shader - * - compile gfx shader items - * - setup required vertex format and render state - * - compile pipeline state object + * @class GrcShaderPermutation + * @brief Defines a particular variant of a compiled shader */ - class GfxPassDesc { - public: + struct GrcShaderPermutation { + static constexpr int MAX_OPTIONS = 64; + + GfxVertAttribs attribs; + std::bitset options; + int pass_idx; }; /** - * @class GfxPass - * @brief Base class for any GPU shaders + * @class GrcShader + * @brief Shader is an instance of shader class for rendering + * + * GrcShader in an instance of shader class. It allows to select a particular pass + * and options for program, configure params, and render geometry, dispatch compute + * shader or configure a particular shader pass. + * + * GrcShader provides pass and options info for gpu program compilation. In some + * cases it may require vertex format as well for rendering passes. * - * Gfx pass is a high level shading program representation. It provides connection - * between a shader, written using glsl in engine source code, optional user defined - * material and a low-level engine gfx api. - * - * Gfx pass provides info about required pipeline layout, allows to obtain final - * shader source code, provides defines info and etc. + * GrcShader may be used itself for internal rendering, or a base for compilation + * of passes for optimized engine models/meshes rendering. */ - class GfxPass { - public: + class GrcShader { }; }// namespace wmoge \ No newline at end of file diff --git a/engine/grc/grc_shader_class.cpp b/engine/grc/grc_shader_class.cpp new file mode 100644 index 000000000..ff2780b24 --- /dev/null +++ b/engine/grc/grc_shader_class.cpp @@ -0,0 +1,135 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#include "grc_shader_class.hpp" + +#include "glsl/glsl_include_processor.hpp" + +#include + +namespace wmoge { + + GrcShaderClass::GrcShaderClass(GrcShaderReflection&& reflection) + : m_reflection(std::move(reflection)) { + } + + Status GrcShaderClass::reload_sources(const std::string& folder, FileSystem* fs) { + fast_vector new_includes; + fast_vector new_sources; + fast_set new_dependencies; + + for (GrcShaderSourceFile& source_file : m_reflection.sources) { + GlslIncludeProcessor include_processor(folder, fs); + + if (!include_processor.parse_file(source_file.name)) { + WG_LOG_ERROR("failed parse file " << source_file.name); + return StatusCode::FailedParse; + } + + for (const Strid& include : include_processor.get_includes()) { + GrcShaderInclude& new_include = new_includes.emplace_back(); + new_include.module = source_file.module; + new_include.name = include; + } + + GrcShaderSourceFile& new_source_file = new_sources.emplace_back(); + new_source_file.name = source_file.name; + new_source_file.module = source_file.module; + new_source_file.content = std::move(include_processor.get_result()); + } + + for (GrcShaderInclude& new_include_file : new_includes) { + new_dependencies.insert(new_include_file.name); + } + + for (GrcShaderSourceFile& new_source_file : new_sources) { + new_dependencies.insert(new_source_file.name); + } + + std::swap(m_reflection.includes, new_includes); + std::swap(m_reflection.sources, new_sources); + std::swap(m_reflection.dependencies, new_dependencies); + + return StatusCode::Ok; + } + + Status GrcShaderClass::fill_layout(GfxDescSetLayoutDesc& desc, int space) const { + assert(0 <= space && space < m_reflection.spaces.size()); + + const GrcShaderSpace& shader_space = m_reflection.spaces[space]; + assert(shader_space.type != GrcShaderSpaceType::Material); + + std::int16_t binding_id = 0; + + for (const GrcShaderBinding& binding : shader_space.bindings) { + GfxDescBinging& gfx_binding = desc.emplace_back(); + gfx_binding.binding = binding_id++; + gfx_binding.count = 1; + gfx_binding.name = binding.name; + + switch (binding.binding) { + case GrcShaderBindingType::InlineUniformBuffer: + case GrcShaderBindingType::UniformBuffer: + gfx_binding.type = GfxBindingType::UniformBuffer; + break; + + case GrcShaderBindingType::Sampler2d: + case GrcShaderBindingType::Sampler2dArray: + case GrcShaderBindingType::SamplerCube: + gfx_binding.type = GfxBindingType::SampledTexture; + break; + + case GrcShaderBindingType::StorageBuffer: + gfx_binding.type = GfxBindingType::StorageBuffer; + break; + + case GrcShaderBindingType::StorageImage2d: + gfx_binding.type = GfxBindingType::StorageImage; + break; + + default: + return StatusCode::InvalidState; + } + } + + return StatusCode::Ok; + } + + bool GrcShaderClass::has_dependency(const Strid& dependency) const { + return m_reflection.dependencies.find(dependency) != m_reflection.dependencies.end(); + } + + bool GrcShaderClass::has_space(GrcShaderSpaceType space_type) const { + for (const GrcShaderSpace& space : m_reflection.spaces) { + if (space.type == space_type) { + return true; + } + } + return false; + } + +}// namespace wmoge \ No newline at end of file diff --git a/engine/grc/grc_shader_class.hpp b/engine/grc/grc_shader_class.hpp new file mode 100644 index 000000000..1025bf8af --- /dev/null +++ b/engine/grc/grc_shader_class.hpp @@ -0,0 +1,70 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#pragma once + +#include "core/status.hpp" +#include "grc/grc_shader_reflection.hpp" +#include "platform/file_system.hpp" + +namespace wmoge { + + /** + * @class GrcShaderClass + * @brief Reprsents a particular shader program class + * + * GrcShaderClass is a high level representation of a shading program. + * It provides a connection between raw glsl sources code of a shader, + * material and engine gfx module for runtime usage. + * + * GrcShaderClass provides info about a particular shader type. It provides + * layout information, parameters and structures layout, defines and + * compilations options, constants and includes, and provides hot-reloading + * mechanism for debugging. + * + * GrcShaderClass is a `template` shader for drawing with pre-defined interface. + * It is not suitable for rendering. In order to get a concrete instance of + * compiled gpu program, pass and options must be provided from GrcShader. + */ + class GrcShaderClass { + public: + GrcShaderClass(GrcShaderReflection&& reflection); + + int set_idx(int idx); + Status reload_sources(const std::string& folder, FileSystem* fs); + Status fill_layout(GfxDescSetLayoutDesc& desc, int space) const; + bool has_dependency(const Strid& dependency) const; + bool has_space(GrcShaderSpaceType space_type) const; + + [[nodiscard]] const GrcShaderReflection& get_reflection() const { return m_reflection; } + + private: + GrcShaderReflection m_reflection; + int m_idx = -1; + }; + +}// namespace wmoge \ No newline at end of file diff --git a/engine/grc/grc_shader_class_builder.cpp b/engine/grc/grc_shader_class_builder.cpp new file mode 100644 index 000000000..4270c13f9 --- /dev/null +++ b/engine/grc/grc_shader_class_builder.cpp @@ -0,0 +1,384 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#include "grc_shader_class_builder.hpp" + +#include "core/string_utils.hpp" + +namespace wmoge { + + GrcShaderClassBuilder::StructBuilder::StructBuilder(GrcShaderClassBuilder& owner, Ref struct_type) + : m_owner(owner), m_struct_type(struct_type) { + } + + GrcShaderClassBuilder::StructBuilder& GrcShaderClassBuilder::StructBuilder::add_field(Strid name, Strid struct_type) { + GrcShaderType::Field& field = m_struct_type->fields.emplace_back(); + field.name = name; + field.type = m_owner.m_reflection.declarations[struct_type]; + field.offset = field.type->byte_size; + return *this; + } + + GrcShaderClassBuilder::StructBuilder& GrcShaderClassBuilder::StructBuilder::add_field(Strid name, Ref type, Var value) { + GrcShaderType::Field& field = m_struct_type->fields.emplace_back(); + field.name = name; + field.type = type; + field.default_value = value; + field.offset = field.type->byte_size; + return *this; + } + + GrcShaderClassBuilder::StructBuilder& GrcShaderClassBuilder::StructBuilder::add_field_array(Strid name, Strid struct_type, int n_elements) { + GrcShaderType::Field& field = m_struct_type->fields.emplace_back(); + field.name = name; + field.type = m_owner.m_reflection.declarations[struct_type]; + field.is_array = true; + field.n_array_elem = n_elements; + field.offset = n_elements * field.type->byte_size; + return *this; + } + + GrcShaderClassBuilder::StructBuilder& GrcShaderClassBuilder::StructBuilder::add_field_array(Strid name, Ref type, Var value, int n_elements) { + GrcShaderType::Field& field = m_struct_type->fields.emplace_back(); + field.name = name; + field.type = type; + field.default_value = value; + field.is_array = true; + field.n_array_elem = n_elements; + field.offset = n_elements * field.type->byte_size; + return *this; + } + + GrcShaderClassBuilder& GrcShaderClassBuilder::StructBuilder::end_struct() { + return m_owner; + } + + GrcShaderClassBuilder::SpaceBuilder::SpaceBuilder(GrcShaderClassBuilder& owner, GrcShaderSpace& space) + : m_owner(owner), m_space(space) { + } + + GrcShaderClassBuilder::SpaceBuilder& GrcShaderClassBuilder::SpaceBuilder::add_inline_uniform_buffer(Strid name, Strid type_struct) { + GrcShaderBinding& binding = m_space.bindings.emplace_back(); + binding.name = name; + binding.binding = GrcShaderBindingType::InlineUniformBuffer; + binding.type = m_owner.m_reflection.declarations[type_struct]; + binding.qualifiers.std140 = true; + return *this; + } + + GrcShaderClassBuilder::SpaceBuilder& GrcShaderClassBuilder::SpaceBuilder::add_uniform_buffer(Strid name, Strid type_struct) { + GrcShaderBinding& binding = m_space.bindings.emplace_back(); + binding.name = name; + binding.binding = GrcShaderBindingType::UniformBuffer; + binding.type = m_owner.m_reflection.declarations[type_struct]; + binding.qualifiers.std140 = true; + return *this; + } + + GrcShaderClassBuilder::SpaceBuilder& GrcShaderClassBuilder::SpaceBuilder::add_texture_2d(Strid name, Ref texture, Ref sampler) { + GrcShaderBinding& binding = m_space.bindings.emplace_back(); + binding.name = name; + binding.binding = GrcShaderBindingType::Sampler2d; + binding.type = GrcShaderTypes::SAMPLER2D; + binding.default_tex = texture; + binding.default_sampler = sampler; + return *this; + } + + GrcShaderClassBuilder::SpaceBuilder& GrcShaderClassBuilder::SpaceBuilder::add_texture_2d_array(Strid name, Ref texture, Ref sampler) { + GrcShaderBinding& binding = m_space.bindings.emplace_back(); + binding.name = name; + binding.binding = GrcShaderBindingType::Sampler2dArray; + binding.type = GrcShaderTypes::SAMPLER2D_ARRAY; + binding.default_tex = texture; + binding.default_sampler = sampler; + return *this; + } + + GrcShaderClassBuilder::SpaceBuilder& GrcShaderClassBuilder::SpaceBuilder::add_texture_cube(Strid name, Ref texture, Ref sampler) { + GrcShaderBinding& binding = m_space.bindings.emplace_back(); + binding.name = name; + binding.binding = GrcShaderBindingType::SamplerCube; + binding.type = GrcShaderTypes::SAMPLER_CUBE; + binding.default_tex = texture; + binding.default_sampler = sampler; + return *this; + } + + GrcShaderClassBuilder::SpaceBuilder& GrcShaderClassBuilder::SpaceBuilder::add_storage_buffer(Strid name, Strid type_struct) { + GrcShaderBinding& binding = m_space.bindings.emplace_back(); + binding.name = name; + binding.binding = GrcShaderBindingType::StorageBuffer; + binding.type = m_owner.m_reflection.declarations[type_struct]; + binding.qualifiers.std430 = true; + return *this; + } + + GrcShaderClassBuilder::SpaceBuilder& GrcShaderClassBuilder::SpaceBuilder::add_storage_image_2d(Strid name) { + GrcShaderBinding& binding = m_space.bindings.emplace_back(); + binding.name = name; + binding.binding = GrcShaderBindingType::StorageImage2d; + return *this; + } + + GrcShaderClassBuilder& GrcShaderClassBuilder::SpaceBuilder::end_space() { + return m_owner; + } + + GrcShaderClassBuilder::PassBuilder::PassBuilder(GrcShaderClassBuilder& owner, GrcShaderPass& pass) + : m_owner(owner), m_pass(pass) { + } + + GrcShaderClassBuilder::PassBuilder& GrcShaderClassBuilder::PassBuilder::add_option(Strid name, fast_vector variants) { + GrcShaderOption& option = m_pass.options.emplace_back(); + option.name = name; + option.variants = std::move(variants); + option.mappings.resize(option.variants.size()); + + m_owner.m_reflection.options_map[name] = m_owner.m_next_option_idx++; + + for (int i = 0; i < int(option.variants.size()); i++) { + option.mappings[i] = m_owner.m_next_variant_idx++; + m_owner.m_reflection.variants_map[option.variants[i]] = option.mappings[i]; + } + + return *this; + } + + GrcShaderClassBuilder& GrcShaderClassBuilder::PassBuilder::end_pass() { + return m_owner; + } + + GrcShaderClassBuilder& GrcShaderClassBuilder::set_name(Strid name) { + m_reflection.shader_name = name; + return *this; + } + + GrcShaderClassBuilder& GrcShaderClassBuilder::add_source(Strid file, GfxShaderModule module) { + GrcShaderSourceFile& source_file = m_reflection.sources.emplace_back(); + source_file.name = file; + source_file.module = module; + return *this; + } + + GrcShaderClassBuilder& GrcShaderClassBuilder::add_constant(Strid name, Var value) { + GrcShaderConstant& constant = m_reflection.constants.emplace_back(); + constant.name = name; + constant.str = value.to_string(); + constant.value = std::move(value); + return *this; + } + + GrcShaderClassBuilder& GrcShaderClassBuilder::add_option(Strid name, fast_vector variants) { + GrcShaderOption& option = m_reflection.options.emplace_back(); + option.name = name; + option.variants = std::move(variants); + option.mappings.resize(option.variants.size()); + + m_reflection.options_map[name] = m_next_option_idx++; + + for (int i = 0; i < int(option.variants.size()); i++) { + option.mappings[i] = m_next_variant_idx++; + m_reflection.variants_map[option.variants[i]] = option.mappings[i]; + } + + return *this; + } + + GrcShaderClassBuilder::StructBuilder GrcShaderClassBuilder::add_struct(Strid name, int byte_size) { + Ref struct_type = make_ref(); + struct_type->name = name; + struct_type->type = GrcShaderBaseType::Struct; + struct_type->byte_size = byte_size; + m_reflection.declarations[name] = struct_type; + return StructBuilder(*this, struct_type); + } + + GrcShaderClassBuilder::SpaceBuilder GrcShaderClassBuilder::add_space(Strid name, GrcShaderSpaceType type) { + GrcShaderSpace& space = m_reflection.spaces.emplace_back(); + space.name = name; + space.type = type; + return SpaceBuilder(*this, space); + } + + GrcShaderClassBuilder::PassBuilder GrcShaderClassBuilder::add_pass(Strid name) { + GrcShaderPass& pass = m_reflection.passes.emplace_back(); + pass.name = name; + m_reflection.passes_map[name] = m_next_pass_idx++; + return PassBuilder(*this, pass); + } + + Status GrcShaderClassBuilder::finish(std::shared_ptr& shader_class) { + for (const auto& [name, type] : m_reflection.declarations) { + int byte_size = 0; + + for (const auto& f : type->fields) { + byte_size += f.offset; + } + + if (byte_size != type->byte_size) { + WG_LOG_ERROR("invalid size for type " + << name + << " exp=" << type->byte_size + << " actual=" << byte_size + << " in " << m_reflection.shader_name); + return StatusCode::Error; + } + + if ((byte_size % int(sizeof(Vec4f))) != 0) { + WG_LOG_ERROR("invalid alignment of type " + << name + << " size=" << byte_size + << " in " << m_reflection.shader_name); + return StatusCode::Error; + } + } + + std::int16_t space_idx = 0; + std::int16_t buffer_idx = 0; + + for (const auto& space : m_reflection.spaces) { + std::int16_t binding_idx = 0; + + for (const auto& binding : space.bindings) { + switch (binding.binding) { + case GrcShaderBindingType::InlineUniformBuffer: { + GrcShaderBufferInfo& buffer = m_reflection.buffers.emplace_back(); + buffer.space = space_idx; + buffer.binding = binding_idx; + + std::int16_t offset = 0; + + for (const auto& field : binding.type->fields) { + if (field.is_array && field.n_array_elem == 0) { + WG_LOG_ERROR("no size array not allowed in " + << " name=" << binding.name + << " in " << m_reflection.shader_name); + return StatusCode::Error; + } + + const std::string param_name_base = binding.name.str() + "." + field.name.str(); + const int params_to_add = field.is_array ? field.n_array_elem : 1; + + const Var& default_var = field.default_value; + Array default_vars; + + if (field.is_array) { + default_vars = std::move(default_var.operator wmoge::Array()); + } + + for (int i = 0; i < params_to_add; i++) { + std::string param_name = param_name_base; + + if (field.is_array) { + param_name += "[" + StringUtils::from_int(i) + "]"; + } + + GrcShaderParamInfo& param = m_reflection.params_info.emplace_back(); + param.name = SID(param_name); + param.type = field.type; + param.space = space_idx; + param.binding = binding_idx; + param.buffer = buffer_idx; + param.offset = offset; + param.default_var = i < default_vars.size() ? default_vars[i] : default_var; + param.default_value_str = param.default_var.to_string(); + param.binding_type = binding.binding; + + offset += field.type->byte_size; + } + } + + if (offset == 0) { + WG_LOG_ERROR("empty inline uniform buffer not allowed " + << " name=" << binding.name + << " in " << m_reflection.shader_name); + return StatusCode::Error; + } + + if (offset != binding.type->byte_size) { + WG_LOG_ERROR("error in params layout " + << " layout size=" << offset + << " actual size=" << binding.type->byte_size + << " in " << m_reflection.shader_name); + return StatusCode::Error; + } + + buffer.size = offset; + buffer_idx++; + break; + } + + case GrcShaderBindingType::Sampler2d: + case GrcShaderBindingType::Sampler2dArray: + case GrcShaderBindingType::SamplerCube: { + GrcShaderParamInfo& param = m_reflection.params_info.emplace_back(); + param.name = binding.name; + param.type = binding.type; + param.space = space_idx; + param.binding = binding_idx; + param.default_tex = binding.default_tex; + param.default_sampler = binding.default_sampler; + param.default_value_str = binding.default_tex->name().str(); + param.binding_type = binding.binding; + break; + } + + case GrcShaderBindingType::UniformBuffer: + case GrcShaderBindingType::StorageBuffer: + case GrcShaderBindingType::StorageImage2d: { + GrcShaderParamInfo& param = m_reflection.params_info.emplace_back(); + param.name = binding.name; + param.type = binding.type; + param.space = space_idx; + param.binding = binding_idx; + param.binding_type = binding.binding; + break; + } + + default: + return StatusCode::InvalidState; + } + + binding_idx++; + } + + space_idx++; + } + + int param_idx = 0; + + for (auto& param : m_reflection.params_info) { + m_reflection.params_id[param.name] = param_idx++; + } + + shader_class = std::make_shared(std::move(m_reflection)); + + return StatusCode::Ok; + } + +}// namespace wmoge \ No newline at end of file diff --git a/engine/grc/grc_shader_class_builder.hpp b/engine/grc/grc_shader_class_builder.hpp new file mode 100644 index 000000000..d44d6a13b --- /dev/null +++ b/engine/grc/grc_shader_class_builder.hpp @@ -0,0 +1,115 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#pragma once + +#include "grc/grc_shader_class.hpp" +#include "grc/grc_shader_reflection.hpp" + +#include + +namespace wmoge { + + /** + * @class GrcShaderClassBuilder + * @brief Allows to configure a particular shader class and register in the engine + */ + class GrcShaderClassBuilder { + public: + class StructBuilder { + public: + StructBuilder(GrcShaderClassBuilder& owner, Ref struct_type); + + StructBuilder& add_field(Strid name, Strid struct_type); + StructBuilder& add_field(Strid name, Ref type, Var value = Var()); + StructBuilder& add_field_array(Strid name, Strid struct_type, int n_elements = 0); + StructBuilder& add_field_array(Strid name, Ref type, Var value = Var(), int n_elements = 0); + GrcShaderClassBuilder& end_struct(); + + private: + GrcShaderClassBuilder& m_owner; + Ref m_struct_type; + + friend class GrcShaderClassBuilder; + }; + + class SpaceBuilder { + public: + SpaceBuilder(GrcShaderClassBuilder& owner, GrcShaderSpace& space); + + SpaceBuilder& add_inline_uniform_buffer(Strid name, Strid type_struct); + SpaceBuilder& add_uniform_buffer(Strid name, Strid type_struct); + SpaceBuilder& add_texture_2d(Strid name, Ref texture, Ref sampler); + SpaceBuilder& add_texture_2d_array(Strid name, Ref texture, Ref sampler); + SpaceBuilder& add_texture_cube(Strid name, Ref texture, Ref sampler); + SpaceBuilder& add_storage_buffer(Strid name, Strid type_struct); + SpaceBuilder& add_storage_image_2d(Strid name); + GrcShaderClassBuilder& end_space(); + + private: + GrcShaderClassBuilder& m_owner; + GrcShaderSpace& m_space; + + friend class GrcShaderClassBuilder; + }; + + class PassBuilder { + public: + PassBuilder(GrcShaderClassBuilder& owner, GrcShaderPass& pass); + + PassBuilder& add_option(Strid name, fast_vector variants); + GrcShaderClassBuilder& end_pass(); + + private: + GrcShaderClassBuilder& m_owner; + GrcShaderPass& m_pass; + + friend class GrcShaderClassBuilder; + }; + + GrcShaderClassBuilder& set_name(Strid name); + GrcShaderClassBuilder& add_source(Strid file, GfxShaderModule module); + GrcShaderClassBuilder& add_constant(Strid name, Var value); + GrcShaderClassBuilder& add_option(Strid name, fast_vector variants); + StructBuilder add_struct(Strid name, int byte_size); + SpaceBuilder add_space(Strid name, GrcShaderSpaceType type); + PassBuilder add_pass(Strid name); + + Status finish(std::shared_ptr& shader_class); + + private: + GrcShaderReflection m_reflection; + int m_next_option_idx = 0; + int m_next_variant_idx = 0; + int m_next_pass_idx = 0; + + friend class StructBuilder; + friend class SpaceBuilder; + friend class PassBuilder; + }; + +}// namespace wmoge \ No newline at end of file diff --git a/engine/grc/grc_shader_manager.cpp b/engine/grc/grc_shader_manager.cpp new file mode 100644 index 000000000..4f89e9f8a --- /dev/null +++ b/engine/grc/grc_shader_manager.cpp @@ -0,0 +1,32 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#include "grc_shader_manager.hpp" + +namespace wmoge { + +} \ No newline at end of file diff --git a/engine/gfx/gfx_pass.cpp b/engine/grc/grc_shader_manager.hpp similarity index 98% rename from engine/gfx/gfx_pass.cpp rename to engine/grc/grc_shader_manager.hpp index 7fcec4f1c..6f9e4df95 100644 --- a/engine/gfx/gfx_pass.cpp +++ b/engine/grc/grc_shader_manager.hpp @@ -23,4 +23,6 @@ /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ /* SOFTWARE. */ -/**********************************************************************************/ \ No newline at end of file +/**********************************************************************************/ + +#pragma once \ No newline at end of file diff --git a/engine/grc/grc_shader_reflection.cpp b/engine/grc/grc_shader_reflection.cpp new file mode 100644 index 000000000..fe0fa0c5f --- /dev/null +++ b/engine/grc/grc_shader_reflection.cpp @@ -0,0 +1,80 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#include "grc_shader_reflection.hpp" + +namespace wmoge { + + static Ref make(GrcShaderBaseType base_type, Strid name) { + Ref t = make_ref(); + t->name = name; + t->type = base_type; + t->byte_size = GrcShaderBaseTypeSizes[int(base_type)]; + return t; + } + static Ref make(GrcShaderBaseType base_type, Strid name, int n_rows) { + Ref t = make_ref(); + t->name = name; + t->type = base_type; + t->n_row = n_rows; + t->n_col = 1; + t->n_elem = n_rows * 1; + t->byte_size = GrcShaderBaseTypeSizes[int(base_type)] * t->n_elem; + return t; + } + static Ref make(GrcShaderBaseType base_type, Strid name, int n_rows, int n_cols) { + Ref t = make_ref(); + t->name = name; + t->type = base_type; + t->n_row = n_rows; + t->n_col = n_cols; + t->n_elem = n_rows * n_cols; + t->byte_size = GrcShaderBaseTypeSizes[int(base_type)] * t->n_elem; + return t; + } + + Ref GrcShaderTypes::FLOAT = make(GrcShaderBaseType::Float, SID("float")); + Ref GrcShaderTypes::INT = make(GrcShaderBaseType::Int, SID("int")); + Ref GrcShaderTypes::BOOL = make(GrcShaderBaseType::Bool, SID("bool")); + Ref GrcShaderTypes::VEC2 = make(GrcShaderBaseType::Float, SID("vec2"), 2); + Ref GrcShaderTypes::VEC3 = make(GrcShaderBaseType::Float, SID("vec3"), 3); + Ref GrcShaderTypes::VEC4 = make(GrcShaderBaseType::Float, SID("vec4"), 4); + Ref GrcShaderTypes::IVEC2 = make(GrcShaderBaseType::Int, SID("ivec2"), 2); + Ref GrcShaderTypes::IVEC3 = make(GrcShaderBaseType::Int, SID("ivec3"), 3); + Ref GrcShaderTypes::IVEC4 = make(GrcShaderBaseType::Int, SID("ivec4"), 4); + Ref GrcShaderTypes::BVEC2 = make(GrcShaderBaseType::Bool, SID("bvec2"), 2); + Ref GrcShaderTypes::BVEC3 = make(GrcShaderBaseType::Bool, SID("bvec3"), 3); + Ref GrcShaderTypes::BVEC4 = make(GrcShaderBaseType::Bool, SID("bvec4"), 4); + Ref GrcShaderTypes::MAT2 = make(GrcShaderBaseType::Float, SID("mat2"), 2, 2); + Ref GrcShaderTypes::MAT3 = make(GrcShaderBaseType::Float, SID("mat3"), 3, 3); + Ref GrcShaderTypes::MAT4 = make(GrcShaderBaseType::Float, SID("mat4"), 4, 4); + Ref GrcShaderTypes::SAMPLER2D = make(GrcShaderBaseType::Sampler2d, SID("sampler2D")); + Ref GrcShaderTypes::SAMPLER2D_ARRAY = make(GrcShaderBaseType::Sampler2dArray, SID("sampler2DArray")); + Ref GrcShaderTypes::SAMPLER_CUBE = make(GrcShaderBaseType::SamplerCube, SID("samplerCube")); + Ref GrcShaderTypes::IMAGE2D = make(GrcShaderBaseType::Image2d, SID("image2D")); + +}// namespace wmoge \ No newline at end of file diff --git a/engine/grc/grc_shader_reflection.hpp b/engine/grc/grc_shader_reflection.hpp new file mode 100644 index 000000000..eeeba9c84 --- /dev/null +++ b/engine/grc/grc_shader_reflection.hpp @@ -0,0 +1,293 @@ +/**********************************************************************************/ +/* Wmoge game engine */ +/* Available at github https://github.com/EgorOrachyov/wmoge */ +/**********************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023 Egor Orachyov */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/**********************************************************************************/ + +#pragma once + +#include "core/fast_map.hpp" +#include "core/fast_set.hpp" +#include "core/fast_vector.hpp" +#include "core/mask.hpp" +#include "core/ref.hpp" +#include "core/string_id.hpp" +#include "core/var.hpp" +#include "gfx/gfx_defs.hpp" +#include "gfx/gfx_sampler.hpp" +#include "gfx/gfx_texture.hpp" +#include "platform/file_system.hpp" +#include "resource/shader.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace wmoge { + + /** + * @brief Base (built-in) types for compositing shader interface + */ + enum class GrcShaderBaseType { + None, + Int, + Float, + Bool, + Struct, + Sampler2d, + Sampler2dArray, + SamplerCube, + Image2d + }; + + /** + * @brief Sizes of of base types + */ + static constexpr const int GrcShaderBaseTypeSizes[] = { + 0, + 4, + 4, + 4, + 0, + 0, + 0, + 0, + 0}; + + /** + * @brief Binding types supported by shader pass interface + */ + enum class GrcShaderBindingType { + None, + InlineUniformBuffer, + UniformBuffer, + StorageBuffer, + Sampler2d, + Sampler2dArray, + SamplerCube, + StorageImage2d + }; + + /** + * @class GrcShaderType + * @brief Recursive complex type for declaring of anything in a shader what has a type + */ + struct GrcShaderType : public RefCnt { + struct Field { + Strid name; + Ref type; + int offset = -1; + int n_array_elem = 0; + bool is_array = false; + Var default_value; + }; + + Strid name; + GrcShaderBaseType type = GrcShaderBaseType::None; + int n_row = -1; + int n_col = -1; + int n_elem = -1; + int byte_size = 0; + std::vector fields; + }; + + /** + * @class GrcShaderTypes + * @brief Pre-defined common shader types + */ + struct GrcShaderTypes { + static Ref FLOAT; + static Ref INT; + static Ref BOOL; + static Ref VEC2; + static Ref VEC3; + static Ref VEC4; + static Ref IVEC2; + static Ref IVEC3; + static Ref IVEC4; + static Ref BVEC2; + static Ref BVEC3; + static Ref BVEC4; + static Ref MAT2; + static Ref MAT3; + static Ref MAT4; + static Ref SAMPLER2D; + static Ref SAMPLER2D_ARRAY; + static Ref SAMPLER_CUBE; + static Ref IMAGE2D; + }; + + /** + * @class GrcShaderConstant + * @brief Declared pass constants inlined as defines into source code + */ + struct GrcShaderConstant { + Strid name; + Var value; + std::string str; + }; + + /** + * @class GrcShaderInclude + * @brief Information about single include file of a shader module + */ + struct GrcShaderInclude { + Strid name; + GfxShaderModule module; + }; + + /** + * @class GrcShaderSourceFile + * @brief Single shader module required for compilation (shader stage) + */ + struct GrcShaderSourceFile { + Strid name; + GfxShaderModule module; + std::string content; + }; + + /** + * @class GrcShaderQualifiers + * @brief Additional qualifiers for shader interface params + */ + struct GrcShaderQualifiers { + bool readonly = false; + bool writeonly = false; + bool std140 = false; + bool std430 = false; + }; + + /** + * @class GrcShaderBinding + * @brief An interface exposed bindable param + */ + struct GrcShaderBinding { + Strid name; + GrcShaderBindingType binding = GrcShaderBindingType::None; + Ref type; + GrcShaderQualifiers qualifiers; + Ref default_tex; + Ref default_sampler; + }; + + /** + * @brief Semantics of a space containing parameters + */ + enum class GrcShaderSpaceType { + Default, + Frame, + Material, + Draw + }; + + /** + * @class GrcShaderSpace + * @brief Contains interface resources for a single descriptor set + */ + struct GrcShaderSpace { + Strid name; + GrcShaderSpaceType type = GrcShaderSpaceType::Default; + fast_vector bindings; + }; + + /** + * @class GrcShaderOption + * @brief An user controlled option which affects shader permutation + */ + struct GrcShaderOption { + Strid name; + fast_vector variants; + fast_vector mappings; + }; + + /** + * @class GrcShaderPass + * @brief Defines single pass of shader, a functional subset + */ + struct GrcShaderPass { + Strid name; + std::vector options; + }; + + /** + * @class GrcShaderParamInfo + * @brief Info about an param which can be set from shader or material + */ + struct GrcShaderParamInfo { + Strid name; + Ref type; + GrcShaderBindingType binding_type = GrcShaderBindingType::None; + std::int16_t space = -1; + std::int16_t binding = -1; + std::int16_t offset = -1; + std::int16_t buffer = -1; + std::string ui_name; + std::string ui_hint; + Var ui_range_min; + Var ui_range_max; + Var default_var; + Ref default_tex; + Ref default_sampler; + std::string default_value_str; + }; + + /** + * @class GrcShaderBufferInfo + * @brief Buffer info for auto packing of scalar params + */ + struct GrcShaderBufferInfo { + std::int16_t space = -1; + std::int16_t binding = -1; + std::int16_t size; + }; + + /** + * @class GrcShaderReflection + * @brief Full reflection information of a single shader class + */ + struct GrcShaderReflection { + Strid shader_name; + fast_map params_id; + fast_vector params_info; + fast_vector buffers; + fast_map> declarations; + fast_vector constants; + fast_vector includes; + fast_vector spaces; + fast_vector sources; + fast_vector passes; + fast_vector options; + fast_map passes_map; + fast_map options_map; + fast_map variants_map; + fast_set dependencies; + }; + +}// namespace wmoge \ No newline at end of file diff --git a/engine/render/camera.cpp b/engine/render/camera.cpp index 392d6b6c8..aa76db9df 100644 --- a/engine/render/camera.cpp +++ b/engine/render/camera.cpp @@ -55,6 +55,15 @@ namespace wmoge { void Camera::set_name(Strid name) { m_name = std::move(name); } + void Camera::set_transform(const Mat4x4f& transform) { + Vec3f position; + Vec3f direction = Vec3f::axis_z(); + Vec3f up = Vec3f::axis_y(); + + position = Math3d::transform(transform, position); + direction = Math3d::transform_w0(transform, direction).normalized(); + up = Math3d::transform_w0(transform, up).normalized(); + } void Camera::look(const Vec3f& dir, const Vec3f& up) { m_direction = dir; m_up = up; diff --git a/engine/render/camera.hpp b/engine/render/camera.hpp index 2f11c4c0b..ad0ed9797 100644 --- a/engine/render/camera.hpp +++ b/engine/render/camera.hpp @@ -58,6 +58,7 @@ namespace wmoge { void set_color(const Color4f& color); void set_proj(CameraProjection proj); void set_name(Strid name); + void set_transform(const Mat4x4f& transform); void look(const Vec3f& dir, const Vec3f& up); void move(const Vec3f& delta); void move_to(const Vec3f& point); diff --git a/engine/render/shader_pass.hpp b/engine/render/shader_pass.hpp index 4aa15f6a1..9559b12b7 100644 --- a/engine/render/shader_pass.hpp +++ b/engine/render/shader_pass.hpp @@ -37,6 +37,9 @@ namespace wmoge { + class ShaderDesc : public RefCnt { + }; + /** * @class ShaderPass * @brief Base class for any engine shading pass diff --git a/engine/scene/scene_components.hpp b/engine/scene/scene_components.hpp index f08783a30..451197304 100644 --- a/engine/scene/scene_components.hpp +++ b/engine/scene/scene_components.hpp @@ -83,9 +83,9 @@ namespace wmoge { struct EcsComponentTransformUpd : EcsComponent { WG_ECS_COMPONENT(EcsComponentTransformUpd); - int batch_id = 0; - int last_frame_updated = -1; - bool is_dirty = true; + int batch_id = 0; + int frame_updated = -1; + bool is_dirty = true; }; /** diff --git a/engine/scene/scene_manager.cpp b/engine/scene/scene_manager.cpp index 4bf131dc4..0087087f8 100644 --- a/engine/scene/scene_manager.cpp +++ b/engine/scene/scene_manager.cpp @@ -27,6 +27,7 @@ #include "scene_manager.hpp" +#include "core/async.hpp" #include "core/task_parallel_for.hpp" #include "debug/profiler.hpp" #include "ecs/ecs_registry.hpp" @@ -66,8 +67,10 @@ namespace wmoge { ecs_registry->register_component(); ecs_registry->register_component(); - m_sys_update_hier = std::make_shared(); - m_sys_release_cull_item = std::make_shared(); + m_sys_update_hier = ecs_registry->register_system(); + m_sys_update_cameras = ecs_registry->register_system(); + m_sys_update_aabb = ecs_registry->register_system(); + m_sys_release_cull_item = ecs_registry->register_system(); } void SceneManager::clear() { @@ -111,15 +114,7 @@ namespace wmoge { Ref SceneManager::make_scene(const Strid& name) { WG_AUTO_PROFILE_SCENE("SceneManager::make_scene"); - auto scene = make_ref(name); - auto* world = scene->get_ecs_world(); - - world->register_system(m_sys_update_hier); - world->register_system(m_sys_release_cull_item); - - m_scenes.push_back(scene); - - return scene; + return m_scenes.emplace_back(make_ref(name)); } std::optional> SceneManager::find_by_name(const Strid& name) { WG_AUTO_PROFILE_SCENE("SceneManager::find_by_name"); @@ -136,28 +131,65 @@ namespace wmoge { void SceneManager::update_scene_hier() { WG_AUTO_PROFILE_SCENE("SceneManager::update_scene_hier"); - Scene* scene = m_running.get(); - EcsWorld* ecs_world = scene->get_ecs_world(); + class HierUpdater : public AsyncState { + public: + explicit HierUpdater(SceneManager* scene_manager) { + m_scene_manager = scene_manager; + } - bool has_dirty_transforms = true; - int current_batch = 0; - int frame_id = scene->get_frame_id(); + void run_next_batch() { + if (!m_has_dirty_transforms) { + set_result(0); + return; + } - while (has_dirty_transforms) { - m_sys_update_hier->current_batch = current_batch; - m_sys_update_hier->frame_id = frame_id; - m_sys_update_hier->num_updated.store(0); - m_sys_update_hier->num_dirty.store(0); + Scene* scene = m_scene_manager->get_running_scene().get(); + EcsWorld* ecs_world = scene->get_ecs_world(); - ecs_world->execute_system(m_sys_update_hier); + std::shared_ptr& sys_update_hier = m_scene_manager->m_sys_update_hier; - has_dirty_transforms = m_sys_update_hier->num_dirty.load() > 0; - current_batch += 1; - } + sys_update_hier->current_batch = m_current_batch; + sys_update_hier->frame_id = scene->get_frame_id(); + sys_update_hier->num_updated.store(0); + sys_update_hier->num_dirty.store(0); + + ecs_world->schedule_system(sys_update_hier).add_dependency(Ref(this)); + } + + void notify(AsyncStatus status, AsyncStateBase* invoker) override { + if (status == AsyncStatus::Failed) { + set_failed(); + return; + } + + std::shared_ptr& sys_update_hier = m_scene_manager->m_sys_update_hier; + + m_has_dirty_transforms = sys_update_hier->num_dirty.load() > 0; + m_current_batch += 1; + + run_next_batch(); + } + + private: + SceneManager* m_scene_manager; + bool m_has_dirty_transforms = true; + int m_current_batch = 0; + }; + + Ref hier_updater = make_ref(this); + hier_updater->run_next_batch(); + + m_sync.complete_heir = AsyncResult(hier_updater).as_async(); } void SceneManager::update_scene_cameras() { WG_AUTO_PROFILE_SCENE("SceneManager::update_scene_cameras"); + + Scene* scene = m_running.get(); + EcsWorld* ecs_world = scene->get_ecs_world(); + + m_sys_update_cameras->frame_id = scene->get_frame_id(); + ecs_world->schedule_system(m_sys_update_cameras, m_sync.complete_heir); } void SceneManager::update_scene_visibility() { @@ -171,7 +203,7 @@ namespace wmoge { CullingManager* vis_system = scene->get_culling_manager(); const CameraList& render_cameras = render_engine->get_cameras(); - vis_system->cull(render_cameras); + // vis_system->cull(render_cameras); } void SceneManager::render_scene() { @@ -245,11 +277,15 @@ namespace wmoge { m_running->advance(Engine::instance()->time()->get_delta_time_game()); + m_sync = SyncContext(); + update_scene_hier(); update_scene_cameras(); update_scene_visibility(); render_scene(); + m_sync.await_all(); + assert(m_running->get_state() == SceneState::Playing); } @@ -271,4 +307,13 @@ namespace wmoge { m_running->set_state(SceneState::Finished); } + void SceneManager::SyncContext::await_all() { + WG_AUTO_PROFILE_SCENE("SyncContext::await_all"); + + if (complete_heir.is_not_null()) complete_heir.wait_completed(); + if (complete_cameras.is_not_null()) complete_cameras.wait_completed(); + if (complete_visibility.is_not_null()) complete_visibility.wait_completed(); + if (complete_render.is_not_null()) complete_render.wait_completed(); + } + }// namespace wmoge \ No newline at end of file diff --git a/engine/scene/scene_manager.hpp b/engine/scene/scene_manager.hpp index 8f34b8258..efafb2759 100644 --- a/engine/scene/scene_manager.hpp +++ b/engine/scene/scene_manager.hpp @@ -27,6 +27,7 @@ #pragma once +#include "core/async.hpp" #include "scene/scene.hpp" #include @@ -75,7 +76,20 @@ namespace wmoge { Ref m_default;// default scene to always show something std::shared_ptr m_sys_update_hier; + std::shared_ptr m_sys_update_cameras; + std::shared_ptr m_sys_update_aabb; std::shared_ptr m_sys_release_cull_item; + + struct SyncContext { + Async complete_heir; + Async complete_cameras; + Async complete_visibility; + Async complete_render; + + void await_all(); + }; + + SyncContext m_sync;// Allows to sync different parts of scene update }; }// namespace wmoge \ No newline at end of file diff --git a/engine/scene/scene_systems.cpp b/engine/scene/scene_systems.cpp index 9ed531c3b..303783cc8 100644 --- a/engine/scene/scene_systems.cpp +++ b/engine/scene/scene_systems.cpp @@ -75,11 +75,34 @@ namespace wmoge { } } - transform_upd.is_dirty = false; - transform_upd.last_frame_updated = frame_id; + transform_upd.is_dirty = false; + transform_upd.frame_updated = frame_id; } - void EcsSysReleaseCullItem::process(EcsWorld& world, const EcsEntity& entity, EcsComponentCullingItem& culling_item) { + void EcsSysUpdateCameras::process(EcsWorld& world, + const EcsEntity& entity, + const EcsComponentTransformUpd& transform_upd, + const EcsComponentLocalToWorld& l2w, + EcsComponentCamera& camera) { + if (transform_upd.frame_updated == frame_id) { + camera.camera.set_transform(l2w.matrix); + } + } + + void EcsSysUpdateAabb::process(EcsWorld& world, + const EcsEntity& entity, + const EcsComponentTransformUpd& transform_upd, + const EcsComponentLocalToWorld& l2w, + const EcsComponentAabbLocal& bbox_local, + EcsComponentAabbWorld& bbox_world) { + if (transform_upd.frame_updated == frame_id) { + bbox_world.aabb = bbox_local.aabb.transform(l2w.matrix); + } + } + + void EcsSysReleaseCullItem::process(EcsWorld& world, + const EcsEntity& entity, + EcsComponentCullingItem& culling_item) { if (!culling_item.item.is_valid()) { return; } diff --git a/engine/scene/scene_systems.hpp b/engine/scene/scene_systems.hpp index fb06a39fe..cddfca840 100644 --- a/engine/scene/scene_systems.hpp +++ b/engine/scene/scene_systems.hpp @@ -44,7 +44,7 @@ namespace wmoge { */ class EcsSysUpdateHier : public EcsSystem { public: - WG_ECS_SYSTEM(EcsSysUpdateHier, Update, OnWorkers); + WG_ECS_SYSTEM(EcsSysUpdateHier, Update, WorkerThreads); void process(EcsWorld& world, const EcsEntity& entity, @@ -61,13 +61,48 @@ namespace wmoge { int frame_id = -1; }; + /** + * @class EcsSysUpdateCameras + * @brief Updates cameras based on transform + */ + class EcsSysUpdateCameras : public EcsSystem { + public: + WG_ECS_SYSTEM(EcsSysUpdateCameras, Update, WorkerThreads); + + void process(EcsWorld& world, + const EcsEntity& entity, + const EcsComponentTransformUpd& transform_upd, + const EcsComponentLocalToWorld& l2w, + EcsComponentCamera& camera); + + int frame_id = -1; + }; + + /** + * @class EcsSysUpdateAabb + * @brief Updates bounding boxes based on transform + */ + class EcsSysUpdateAabb : public EcsSystem { + public: + WG_ECS_SYSTEM(EcsSysUpdateAabb, Update, WorkerThreads); + + void process(EcsWorld& world, + const EcsEntity& entity, + const EcsComponentTransformUpd& transform_upd, + const EcsComponentLocalToWorld& l2w, + const EcsComponentAabbLocal& bbox_local, + EcsComponentAabbWorld& bbox_world); + + int frame_id = -1; + }; + /** * @class EcsSysReleaseCullItem * @brief System to delete cull items */ class EcsSysReleaseCullItem : public EcsSystem { public: - WG_ECS_SYSTEM(EcsSysReleaseCullItem, Destroy, OnMain); + WG_ECS_SYSTEM(EcsSysReleaseCullItem, Destroy, SingleThread); void process(EcsWorld& world, const EcsEntity& entity, diff --git a/template/main.cpp b/template/main.cpp index 152521078..8fdae3fcc 100644 --- a/template/main.cpp +++ b/template/main.cpp @@ -78,6 +78,53 @@ class TemplateApplication : public GameApplication { } WG_LOG_INFO("status for " << variant->name() << " is " << magic_enum::enum_name(variant->status())); + TextureManager* txm = Engine::instance()->texture_manager(); + Ref tex_def = txm->get_gfx_default_texture_white(); + Ref smp_def = txm->get_gfx_default_sampler(); + + GrcShaderClassBuilder builder; + + builder.set_name(SID("canvas")) + .add_constant(SID("MAX_CANVAS_IMAGES"), 4) + .add_struct(SID("Params"), 80) + .add_field(SID("ClipProjView"), GrcShaderTypes::MAT4, TypedArray({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})) + .add_field(SID("InverseGamma"), GrcShaderTypes::FLOAT, 1.0f / 2.2f) + .add_field(SID("_pr_pad0"), GrcShaderTypes::FLOAT, 0.0f) + .add_field(SID("_pr_pad1"), GrcShaderTypes::FLOAT, 0.0f) + .add_field(SID("_pr_pad2"), GrcShaderTypes::FLOAT, 0.0f) + .end_struct() + .add_struct(SID("DrawCmd"), 80) + .add_field(SID("Transform0"), GrcShaderTypes::VEC4) + .add_field(SID("Transform1"), GrcShaderTypes::VEC4) + .add_field(SID("Transform2"), GrcShaderTypes::VEC4) + .add_field(SID("ClipRect"), GrcShaderTypes::VEC4) + .add_field(SID("TextureIdx"), GrcShaderTypes::INT) + .add_field(SID("_dc_pad0"), GrcShaderTypes::INT) + .add_field(SID("_dc_pad1"), GrcShaderTypes::INT) + .add_field(SID("_dc_pad2"), GrcShaderTypes::INT) + .end_struct() + .add_struct(SID("DrawCmds"), 0) + .add_field_array(SID("Cmds"), SID("DrawCmd")) + .end_struct() + .add_space(SID("Default"), GrcShaderSpaceType::Default) + .add_inline_uniform_buffer(SID("Params"), SID("Params")) + .add_storage_buffer(SID("DrawCmds"), SID("DrawCmds")) + .end_space() + .add_space(SID("Images"), GrcShaderSpaceType::Default) + .add_texture_2d(SID("CanvasImage0"), tex_def, smp_def) + .add_texture_2d(SID("CanvasImage1"), tex_def, smp_def) + .add_texture_2d(SID("CanvasImage2"), tex_def, smp_def) + .add_texture_2d(SID("CanvasImage3"), tex_def, smp_def) + .end_space() + .add_pass(SID("Default")) + .add_option(SID("OUT_COLOR"), {SID("SRGB"), SID("LINEAR")}) + .end_pass() + .add_source(SID("canvas.vert"), GfxShaderModule::Vertex) + .add_source(SID("canvas.frag"), GfxShaderModule::Fragment); + + std::shared_ptr shader_class; + builder.finish(shader_class); + WG_LOG_INFO("init"); return StatusCode::Ok; }