From 9784612024f2e7fc9f9eb052389acc711bdc9465 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 5 Feb 2023 14:51:44 -0800 Subject: [PATCH 01/35] Add WIP Universe scenario --- src/osp/universe/universe.h | 107 ++++++----- src/osp/universe/universetypes.h | 4 +- .../activescenes/identifiers.h | 13 ++ .../activescenes/scenarios.cpp | 53 ++++++ .../activescenes/scene_universe.cpp | 173 ++++++++++++++++++ .../activescenes/scene_universe.h | 51 ++++++ 6 files changed, 353 insertions(+), 48 deletions(-) create mode 100644 src/test_application/activescenes/scene_universe.cpp create mode 100644 src/test_application/activescenes/scene_universe.h diff --git a/src/osp/universe/universe.h b/src/osp/universe/universe.h index f04591b8..6500ec04 100644 --- a/src/osp/universe/universe.h +++ b/src/osp/universe/universe.h @@ -38,44 +38,39 @@ namespace osp::universe { +struct StrideDesc +{ + constexpr bool not_used() const noexcept { return m_stride == 0; } + + std::size_t m_offset{0}; + std::ptrdiff_t m_stride{0}; +}; + /** - * @brief Describes strided vector data within an externally stored buffer - * - * Intended to make different data layouts easier to access, and allows - * interleving different datatypes: - * * { XXXXX ... YYYYY ... ZZZZZ ... } - * * { XYZ XYZ XYZ XYZ XYZ XYZ ... } - * * { XYZ ??? XYZ ??? XYZ ??? ... } + * @brief Describes strided data within an externally stored buffer */ -template -struct SatVectorDesc +template +struct TypedStrideDesc : StrideDesc { using View_t = Corrade::Containers::StridedArrayView1D; using ViewConst_t = Corrade::Containers::StridedArrayView1D; using Data_t = Corrade::Containers::ArrayView; using DataConst_t = Corrade::Containers::ArrayView; - static constexpr std::size_t smc_dimensions = DIMENSIONS_T; - - // Offsets of xyz components in bytes - std::array m_offsets; - - // Assuming xyz all have the same stride - std::ptrdiff_t m_stride; - - constexpr View_t view(Data_t data, std::size_t count, std::size_t dimension) const + constexpr View_t view(Data_t data, std::size_t count) const noexcept { - auto first = reinterpret_cast((data.exceptPrefix(m_offsets[dimension])).data()); - return {data, first, count, m_stride}; + return stridedArrayView(data, reinterpret_cast(&data[m_offset]), count, m_stride); } - constexpr ViewConst_t view(DataConst_t data, std::size_t count, std::size_t dimension) const + constexpr ViewConst_t view(DataConst_t data, std::size_t count) const noexcept { - auto first = reinterpret_cast((data.exceptPrefix(m_offsets[dimension])).data()); - return {data, first, count, m_stride}; + return stridedArrayView(data, reinterpret_cast(&data[m_offset]), count, m_stride); } }; +template +using StrideDescArray_t = std::array, N>; + struct CoSpaceHierarchy { CoSpaceId m_parent{lgrn::id_null()}; @@ -97,35 +92,55 @@ struct CoSpaceSatData uint32_t m_satCount; uint32_t m_satCapacity; - Corrade::Containers::Array m_data; - SatVectorDesc m_satPositions; - SatVectorDesc m_satVelocities; - SatVectorDesc m_satRotations; + Corrade::Containers::Array m_data; + + // Describes m_data + StrideDescArray_t m_satPositions; + StrideDescArray_t m_satVelocities; + StrideDescArray_t m_satRotations; }; + struct CoSpaceCommon : CoSpaceTransform, CoSpaceHierarchy, CoSpaceSatData { }; -struct RedesignateSat +struct Universe { - SatId m_old; - SatId m_new; + Universe() = default; + Universe(Universe const&) = delete; + Universe(Universe&& move) = default; + + lgrn::IdRegistryStl m_coordIds; + + std::vector m_coordCommon; }; -struct TransferSat +struct SceneFrame : CoSpaceTransform, CoSpaceHierarchy { - SatId m_satOld; - SatId m_satNew; - CoSpaceId m_coordOld; - CoSpaceId m_coordNew; }; -struct Universe +template +constexpr void aux_interleave(std::size_t const pos, TypedStrideDesc& rStrideDescFirst, TypedStrideDesc& ... rStrideDesc) { - lgrn::IdRegistryStl m_coordIds; + rStrideDescFirst.m_offset = pos; - std::vector m_coordCommon; -}; + if constexpr (sizeof...(T) != 0) + { + aux_interleave(pos + sizeof(FIRST_T), rStrideDesc ...); + } +} + +template +constexpr void interleave(std::size_t& rPos, std::size_t count, TypedStrideDesc& ... rStrideDesc) +{ + constexpr std::size_t stride = (sizeof(T) + ...); + + (rStrideDesc.m_stride = ... = stride); + + aux_interleave(rPos, rStrideDesc ...); + + rPos += stride * count; +} // INDEX_T is a template parameter to allow passing in "strong typedef" types, // like enum classes and having them converted without warning to size_t. @@ -141,28 +156,28 @@ constexpr VEC_T to_vec(INDEX_T i, RANGE_T&& ... args) noexcept /** * @brief Get StridedArrayView1Ds from all components of a SatVectorDesc * - * @param satVec [in] SatVectorDesc describing layout in rData - * @param rData [in] unsigned char* Satellite data, optionally const - * @param satCount [in] Range of valid satellites + * @param strideDescArray [in] Array of stride description for data in rData + * @param rData [in] unsigned char* Satellite data, optionally const + * @param satCount [in] Range of valid satellites * * @return std::array of views, intended to work with structured bindings */ template constexpr auto sat_views( - SatVectorDesc const& satVec, + StrideDescArray_t const& strideDescArray, DATA_T &rData, std::size_t satCount) noexcept { // Recursive call to make COUNT_T a range of numbers from 0 to DIMENSIONS_T - // This is unpacked into satVec.view(...) to access components + // This is unpacked into strideDescArray[COUNT_T].view(...) to access components constexpr int countUp = sizeof...(COUNT_T); if constexpr (countUp != DIMENSIONS_T) { - return sat_views(satVec, rData, satCount); + return sat_views(strideDescArray, rData, satCount); } else { - return std::array{ satVec.view(Corrade::Containers::arrayView(rData), satCount, COUNT_T) ... }; + return std::array{ strideDescArray[COUNT_T].view(Corrade::Containers::arrayView(rData), satCount) ... }; } } diff --git a/src/osp/universe/universetypes.h b/src/osp/universe/universetypes.h index 618c2796..4396610e 100644 --- a/src/osp/universe/universetypes.h +++ b/src/osp/universe/universetypes.h @@ -33,8 +33,8 @@ namespace osp::universe { -enum class SatId : uint32_t {}; -enum class CoSpaceId : uint32_t {}; +using SatId = uint32_t; +using CoSpaceId = uint32_t; } diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 1aaced08..57bf0961 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -188,6 +188,19 @@ idRocketsNwt +//----------------------------------------------------------------------------- + +// Universe sessions + +#define OSP_DATA_TESTAPP_UNI_CORE 1, \ + idUniverse +#define OSP_TAGS_TESTAPP_UNI_CORE 1, \ + tgUniDummy + +#define OSP_DATA_TESTAPP_UNI_SCENEFRAME 1, \ + idUniScnFrame +#define OSP_TAGS_TESTAPP_UNI_SCENEFRAME 1, \ + tgUniDummyB //----------------------------------------------------------------------------- diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index d846ea50..973dd7e7 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -32,6 +32,7 @@ #include "scene_misc.h" #include "scene_newton.h" #include "scene_renderer.h" +#include "scene_universe.h" #include "scene_vehicles.h" #include "../ActiveApplication.h" @@ -277,6 +278,58 @@ static ScenarioMap_t make_scenarios() }; }); + add_scenario("universe", "Universe test scenario with very unrealistic planets", + [] (MainView mainView, PkgId pkg, Sessions_t& sceneOut) -> RendererSetup_t + { + using namespace testapp::scenes; + + auto const idResources = mainView.m_idResources; + auto &rTopData = mainView.m_topData; + auto &rTags = mainView.m_rTags; + Builder_t builder{rTags, mainView.m_rTasks, mainView.m_rTaskData}; + + sceneOut.resize(13); + auto & [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(sceneOut); + + // Compose together lots of Sessions + scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, pkg); + matVisual = setup_material (builder, rTopData, rTags, scnCommon); + physics = setup_physics (builder, rTopData, rTags, scnCommon); + shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, scnCommon, physics, matVisual); + droppers = setup_droppers (builder, rTopData, rTags, scnCommon, shapeSpawn); + bounds = setup_bounds (builder, rTopData, rTags, scnCommon, physics, shapeSpawn); + + newton = setup_newton (builder, rTopData, rTags, scnCommon, physics); + nwtGravSet = setup_newton_factors (builder, rTopData, rTags); + nwtGrav = setup_newton_force_accel (builder, rTopData, rTags, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); + shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, rTags, scnCommon, physics, shapeSpawn, newton, nwtGravSet); + + uniCore = setup_uni_core (builder, rTopData, rTags); + uniScnFrame = setup_uni_sceneframe (builder, rTopData, rTags); + uniTestPlanets = setup_uni_test_planets (builder, rTopData, rTags, uniCore, uniScnFrame); + + add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, pkg); + + return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, [[maybe_unused]] Sessions_t& rendererOut) + { + auto &rTopData = mainView.m_topData; + auto &rTags = mainView.m_rTags; + Builder_t builder{mainView.m_rTags, mainView.m_rTasks, mainView.m_rTaskData}; + + auto const& [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(scene); + + rendererOut.resize(5); + auto & [scnRender, cameraCtrl, cameraFree, shVisual, camThrow] = unpack<5>(rendererOut); + scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); + cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); + cameraFree = setup_camera_free (builder, rTopData, rTags, magnum, scnCommon, cameraCtrl); + shVisual = setup_shader_visualizer (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); + camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); + + setup_magnum_draw(mainView, magnum, scnCommon, scnRender); + }; + }); + return scenarioMap; } diff --git a/src/test_application/activescenes/scene_universe.cpp b/src/test_application/activescenes/scene_universe.cpp new file mode 100644 index 00000000..8673dbc6 --- /dev/null +++ b/src/test_application/activescenes/scene_universe.cpp @@ -0,0 +1,173 @@ +/** + * Open Space Program + * Copyright © 2019-2022 Open Space Program Project + * + * MIT License + * + * 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 "scene_universe.h" + +#include "identifiers.h" + +#include + +#include + +using namespace osp; +using namespace osp::universe; + +namespace testapp::scenes +{ + + +Session setup_uni_core( + Builder_t& rBuilder, + ArrayView topData, + Tags& rTags) +{ + Session uniCore; + OSP_SESSION_ACQUIRE_DATA(uniCore, topData, TESTAPP_UNI_CORE); + OSP_SESSION_ACQUIRE_TAGS(uniCore, rTags, TESTAPP_UNI_CORE); + + top_emplace< Universe > (topData, idUniverse); + + return uniCore; +} + +Session setup_uni_sceneframe( + Builder_t& rBuilder, + ArrayView topData, + Tags& rTags) +{ + Session uniScnFrame; + OSP_SESSION_ACQUIRE_DATA(uniScnFrame, topData, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_ACQUIRE_TAGS(uniScnFrame, rTags, TESTAPP_UNI_SCENEFRAME); + + top_emplace< SceneFrame > (topData, idUniScnFrame); + + return uniScnFrame; +} + + +Session setup_uni_test_planets( + Builder_t& rBuilder, + ArrayView topData, + Tags& rTags, + Session const& uniCore, + Session const& uniScnFrame) +{ + using Corrade::Containers::Array; + + OSP_SESSION_UNPACK_TAGS(uniCore, TESTAPP_UNI_CORE); + OSP_SESSION_UNPACK_DATA(uniCore, TESTAPP_UNI_CORE); + OSP_SESSION_UNPACK_TAGS(uniScnFrame, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_UNPACK_DATA(uniScnFrame, TESTAPP_UNI_SCENEFRAME); + + auto &rUniverse = top_get< Universe >(topData, idUniverse); + + Session uniTestPlanets; + + constexpr int precision = 10; + constexpr int planetCount = 64; + constexpr int seed = 1337; + constexpr spaceint_t maxDist = 20000ul << precision; + constexpr float maxVel = 800.0f; + + constexpr std::size_t planetSize = sizeof(spaceint_t) * 3 // Position + + sizeof(double) * 3 // Velocity + + sizeof(double) * 4; // Rotation + + // Create coordinate spaces + CoSpaceId const mainSpace = rUniverse.m_coordIds.create(); + std::vector surfaceSpaces(planetCount); + rUniverse.m_coordIds.create(surfaceSpaces.begin(), surfaceSpaces.end()); + + rUniverse.m_coordCommon.resize(rUniverse.m_coordIds.capacity()); + + CoSpaceCommon &rMainSpaceCommon = rUniverse.m_coordCommon[mainSpace]; + rMainSpaceCommon.m_satCount = planetCount; + rMainSpaceCommon.m_satCapacity = planetCount; + + // Associate each planet satellite with their surface coordinate space + for (SatId satId = 0; satId < planetCount; ++satId) + { + CoSpaceId const surfaceSpaceId = surfaceSpaces[satId]; + CoSpaceCommon &rCommon = rUniverse.m_coordCommon[surfaceSpaceId]; + rCommon.m_parent = mainSpace; + rCommon.m_parentSat = satId; + } + + // Create description for Position, Velocity, and Rotation data + // TODO: Alignment is needed for SIMD. see Corrade alignedAlloc + + std::size_t bytesUsed = 0; + + // Positions and velocities are arranged as XXXX... YYYY... ZZZZ... + interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satPositions[0]); + interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satPositions[1]); + interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satPositions[2]); + interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satVelocities[0]); + interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satVelocities[1]); + interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satVelocities[2]); + + // Rotations use XYZWXYZWXYZWXYZW... + interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satRotations[0], + rMainSpaceCommon.m_satRotations[1], + rMainSpaceCommon.m_satRotations[2], + rMainSpaceCommon.m_satRotations[3]); + + // Allocate data for all planets + rMainSpaceCommon.m_data = Array{Corrade::NoInit, bytesUsed}; + + // Create easily accessible array views for each component + auto const [x, y, z] = sat_views(rMainSpaceCommon.m_satPositions, rMainSpaceCommon.m_data, planetCount); + auto const [vx, vy, vz] = sat_views(rMainSpaceCommon.m_satVelocities, rMainSpaceCommon.m_data, planetCount); + auto const [qx, qy, qz, qw] = sat_views(rMainSpaceCommon.m_satRotations, rMainSpaceCommon.m_data, planetCount); + + std::mt19937 gen(seed); + std::uniform_int_distribution posDist(-maxDist, maxDist); + std::uniform_real_distribution velDist(-maxVel, maxVel); + + for (std::size_t i = 0; i < planetCount; ++i) + { + // Assign each planet random ositions and velocities + x[i] = posDist(gen); + y[i] = posDist(gen); + z[i] = posDist(gen); + vx[i] = velDist(gen); + vy[i] = velDist(gen); + vz[i] = velDist(gen); + + // No rotation + qx[i] = 0.0; + qy[i] = 0.0; + qz[i] = 0.0; + qw[i] = 1.0; + } + + return uniTestPlanets; +} + +} + + + + diff --git a/src/test_application/activescenes/scene_universe.h b/src/test_application/activescenes/scene_universe.h new file mode 100644 index 00000000..25fc7935 --- /dev/null +++ b/src/test_application/activescenes/scene_universe.h @@ -0,0 +1,51 @@ +/** + * Open Space Program + * Copyright © 2019-2022 Open Space Program Project + * + * MIT License + * + * 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 "scenarios.h" + +namespace testapp::scenes +{ + +osp::Session setup_uni_core( + Builder_t& rBuilder, + osp::ArrayView topData, + osp::Tags& rTags); + + +osp::Session setup_uni_sceneframe( + Builder_t& rBuilder, + osp::ArrayView topData, + osp::Tags& rTags); + +osp::Session setup_uni_test_planets( + Builder_t& rBuilder, + osp::ArrayView topData, + osp::Tags& rTags, + osp::Session const& uniCore, + osp::Session const& uniScnFrame); + + +} From 6d97a52d36895653f0baede4d65317ed983ff0ba Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Thu, 2 Mar 2023 20:57:11 -0800 Subject: [PATCH 02/35] Re-add universe test scene --- src/osp/Shaders/Flat.cpp | 49 ---- src/osp/Shaders/Flat.h | 69 ++++- src/osp/Shaders/Phong.h | 8 +- src/osp/types.h | 1 + src/osp/universe/universe.h | 14 +- .../activescenes/identifiers.h | 36 ++- .../activescenes/scenarios.cpp | 39 +-- .../activescenes/scene_physics.cpp | 2 +- .../activescenes/scene_renderer.cpp | 277 ++++++++++++++++-- .../activescenes/scene_renderer.h | 34 +++ .../activescenes/scene_universe.cpp | 195 +++++++++--- src/test_application/main.cpp | 1 + 12 files changed, 562 insertions(+), 163 deletions(-) diff --git a/src/osp/Shaders/Flat.cpp b/src/osp/Shaders/Flat.cpp index 933def2a..f38c3881 100644 --- a/src/osp/Shaders/Flat.cpp +++ b/src/osp/Shaders/Flat.cpp @@ -70,52 +70,3 @@ void shader::draw_ent_flat( } -void shader::assign_flat( - RenderGroup::ArrayView_t entities, - RenderGroup::Storage_t *pStorageOpaque, - RenderGroup::Storage_t *pStorageTransparent, - acomp_storage_t const& opaque, - acomp_storage_t const& diffuse, - ACtxDrawFlat &rData) -{ - for (ActiveEnt const ent : entities) - { - if (opaque.contains(ent)) - { - if (pStorageOpaque == nullptr) - { - continue; - } - - if (diffuse.contains(ent)) - { - pStorageOpaque->emplace( - ent, EntityToDraw{&draw_ent_flat, {&rData, &rData.m_shaderDiffuse} }); - } - else - { - pStorageOpaque->emplace( - ent, EntityToDraw{&draw_ent_flat, {&rData, &rData.m_shaderUntextured} }); - } - } - else - { - if (pStorageTransparent == nullptr) - { - continue; - } - - if (diffuse.contains(ent)) - { - pStorageTransparent->emplace( - ent, EntityToDraw{&draw_ent_flat, {&rData, &rData.m_shaderDiffuse} }); - } - else - { - pStorageTransparent->emplace( - ent, EntityToDraw{&draw_ent_flat, {&rData, &rData.m_shaderUntextured} }); - } - } - } -} - diff --git a/src/osp/Shaders/Flat.h b/src/osp/Shaders/Flat.h index a3779198..9c815895 100644 --- a/src/osp/Shaders/Flat.h +++ b/src/osp/Shaders/Flat.h @@ -73,20 +73,69 @@ void draw_ent_flat( * @brief Assign a Flat shader to a set of entities, and write results to * a RenderGroup * - * @param entities [in] Entities to consider + * @param dirtyFirst [in] Iterator to first entity to sync + * @param dirtyLast [in] Iterator to last entity to sync + * @param hasMaterial [in] Which entities a phong material is assigned to * @param pStorageOpaque [out] Optional RenderGroup storage for opaque * @param pStorageTransparent [out] Optional RenderGroup storage for transparent - * @param opaque [in] View for opaque component - * @param diffuse [in] View for diffuse texture component + * @param opaque [in] Storage for opaque component + * @param diffuse [in] Storage for diffuse texture component * @param rData [in] Phong shader data, stable memory required */ -void assign_flat( - active::RenderGroup::ArrayView_t entities, - active::RenderGroup::Storage_t *pStorageOpaque, - active::RenderGroup::Storage_t *pStorageTransparent, - active::acomp_storage_t const& opaque, - active::acomp_storage_t const& diffuse, - ACtxDrawFlat &rData); +template +void sync_flat( + ITA_T dirtyIt, + ITB_T const& dirtyLast, + active::EntSet_t const& hasMaterial, + active::RenderGroup::Storage_t *const pStorageOpaque, + active::RenderGroup::Storage_t *const pStorageTransparent, + active::acomp_storage_t const& opaque, + active::acomp_storage_t const& diffuse, + ACtxDrawFlat &rData) +{ + using namespace active; + + for (; dirtyIt != dirtyLast; std::advance(dirtyIt, 1)) + { + ActiveEnt const ent = *dirtyIt; + + // Erase from group if they exist + if (pStorageOpaque != nullptr) + { + pStorageOpaque->remove(ent); + } + if (pStorageTransparent != nullptr) + { + pStorageTransparent->remove(ent); + } + + if ( ! hasMaterial.test(std::size_t(ent))) + { + continue; // Phong material is not assigned to this entity + } + + Flat *pShader = diffuse.contains(ent) ? &rData.m_shaderDiffuse : &rData.m_shaderUntextured; + + if (opaque.contains(ent)) + { + if (pStorageOpaque == nullptr) + { + continue; + } + + pStorageOpaque->emplace(ent, EntityToDraw{&draw_ent_flat, {&rData, pShader} }); + } + else + { + if (pStorageTransparent == nullptr) + { + continue; + } + + pStorageTransparent->emplace(ent, EntityToDraw{&draw_ent_flat, {&rData, pShader} }); + } + } +} } // namespace osp::shader diff --git a/src/osp/Shaders/Phong.h b/src/osp/Shaders/Phong.h index 7db3f946..d28a2bf1 100644 --- a/src/osp/Shaders/Phong.h +++ b/src/osp/Shaders/Phong.h @@ -85,7 +85,7 @@ void draw_ent_phong( */ template void sync_phong( - ITA_T dirtyFirst, + ITA_T dirtyIt, ITB_T const& dirtyLast, active::EntSet_t const& hasMaterial, active::RenderGroup::Storage_t *const pStorageOpaque, @@ -96,9 +96,9 @@ void sync_phong( { using namespace active; - while (dirtyFirst != dirtyLast) + for (; dirtyIt != dirtyLast; std::advance(dirtyIt, 1)) { - ActiveEnt const ent = *dirtyFirst; + ActiveEnt const ent = *dirtyIt; // Erase from group if they exist if (pStorageOpaque != nullptr) @@ -135,8 +135,6 @@ void sync_phong( pStorageTransparent->emplace(ent, EntityToDraw{&draw_ent_phong, {&rData, pShader} }); } - - std::advance(dirtyFirst, 1); } } diff --git a/src/osp/types.h b/src/osp/types.h index 2dbbb8ea..bd011ffe 100644 --- a/src/osp/types.h +++ b/src/osp/types.h @@ -64,6 +64,7 @@ using Quaternion = Magnum::Math::Quaternion; using Quaterniond = Magnum::Math::Quaternion; using Rad = Magnum::Math::Rad; +using Radd = Magnum::Math::Rad; using Deg = Magnum::Math::Deg; } diff --git a/src/osp/universe/universe.h b/src/osp/universe/universe.h index 6500ec04..d4c3c1d3 100644 --- a/src/osp/universe/universe.h +++ b/src/osp/universe/universe.h @@ -116,28 +116,28 @@ struct Universe struct SceneFrame : CoSpaceTransform, CoSpaceHierarchy { - + Vector3g m_scenePosition; }; template -constexpr void aux_interleave(std::size_t const pos, TypedStrideDesc& rStrideDescFirst, TypedStrideDesc& ... rStrideDesc) +constexpr void aux_partition(std::size_t const pos, TypedStrideDesc& rInterleveFirst, TypedStrideDesc& ... rInterleve) { - rStrideDescFirst.m_offset = pos; + rInterleveFirst.m_offset = pos; if constexpr (sizeof...(T) != 0) { - aux_interleave(pos + sizeof(FIRST_T), rStrideDesc ...); + aux_partition(pos + sizeof(FIRST_T), rInterleve ...); } } template -constexpr void interleave(std::size_t& rPos, std::size_t count, TypedStrideDesc& ... rStrideDesc) +constexpr void partition(std::size_t& rPos, std::size_t count, TypedStrideDesc& ... rInterleve) { constexpr std::size_t stride = (sizeof(T) + ...); - (rStrideDesc.m_stride = ... = stride); + (rInterleve.m_stride = ... = stride); - aux_interleave(rPos, rStrideDesc ...); + aux_partition(rPos, rInterleve ...); rPos += stride * count; } diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 57bf0961..a18680a1 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -192,15 +192,19 @@ // Universe sessions -#define OSP_DATA_TESTAPP_UNI_CORE 1, \ - idUniverse -#define OSP_TAGS_TESTAPP_UNI_CORE 1, \ - tgUniDummy +#define OSP_DATA_TESTAPP_UNI_CORE 2, \ + idUniverse, tgUniDeltaTimeIn +#define OSP_TAGS_TESTAPP_UNI_CORE 4, \ + tgUniUpdEvt, tgUniTimeEvt, \ + tgUniTransferMod, tgUniTransferReq #define OSP_DATA_TESTAPP_UNI_SCENEFRAME 1, \ - idUniScnFrame -#define OSP_TAGS_TESTAPP_UNI_SCENEFRAME 1, \ - tgUniDummyB + idScnFrame +#define OSP_TAGS_TESTAPP_UNI_SCENEFRAME 2, \ + tgScnFramePosMod, tgScnFramePosReq + +#define OSP_DATA_TESTAPP_UNI_PLANETS 2, \ + idPlanetMainSpace, idSatSurfaceSpaces //----------------------------------------------------------------------------- @@ -222,7 +226,7 @@ #define OSP_DATA_TESTAPP_COMMON_RENDERER 3, \ idScnRender, idGroupFwd, idCamera -#define OSP_TAGS_TESTAPP_COMMON_RENDERER 20, \ +#define OSP_TAGS_TESTAPP_COMMON_RENDERER 24, \ tgDrawGlDel, tgDrawGlMod, tgDrawGlReq, \ tgMeshGlMod, tgMeshGlReq, \ tgTexGlMod, tgTexGlReq, \ @@ -230,6 +234,8 @@ tgEntMeshMod, tgEntMeshReq, \ tgCameraMod, tgCameraReq, \ tgGroupFwdDel, tgGroupFwdMod, tgGroupFwdReq, \ + tgBindFboMod, tgBindFboReq, \ + tgFwdRenderMod, tgFwdRenderReq, \ tgDrawTransformDel, tgDrawTransformNew, tgDrawTransformMod, tgDrawTransformReq @@ -242,9 +248,17 @@ #define OSP_DATA_TESTAPP_SHADER_VISUALIZER 1, \ - idDrawVisual -#define OSP_TAGS_TESTAPP_SHADER_VISUALIZER 3, \ - tgRenderEvt, tgInputEvt, tgGlUse + idDrawShVisual + + + +#define OSP_DATA_TESTAPP_SHADER_PHONG 1, \ + idDrawShPhong + + + +#define OSP_DATA_TESTAPP_SHADER_FLAT 1, \ + idDrawShFlat diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 973dd7e7..3889830d 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -54,7 +54,7 @@ using namespace osp; namespace testapp { -static void setup_magnum_draw(MainView mainView, Session const& magnum, Session const& scnCommon, Session const& scnRender) +static void setup_magnum_draw(MainView mainView, Session const& magnum, Session const& scnCommon, Session const& scnRender, std::vector runTagsVec = {}) { OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); @@ -67,18 +67,18 @@ static void setup_magnum_draw(MainView mainView, Session const& magnum, Session ExecutionContext &rExec = mainView.m_rExec; ArrayView topData = mainView.m_topData; - auto &rActiveApp = top_get(topData, idActiveApp); - - auto &rCamera = top_get(topData, idCamera); + auto &rActiveApp = top_get(topData, idActiveApp); + auto &rCamera = top_get(topData, idCamera); rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); - - top_enqueue_quick(rTags, rTasks, rExec, {tgSyncEvt, tgResyncEvt}); + // Run Resync tasks to mark all used gpu resources as dirty + top_enqueue_quick(rTags, rTasks, rExec, {tgResyncEvt}); top_run_blocking(rTags, rTasks, rTaskData, topData, rExec); - auto const runTags = {tgSyncEvt, tgSceneEvt, tgTimeEvt, tgRenderEvt, tgInputEvt}; + runTagsVec.insert(runTagsVec.end(), {tgSyncEvt, tgSceneEvt, tgTimeEvt, tgRenderEvt, tgInputEvt}); - rActiveApp.set_on_draw( [&rTags, &rTasks, &rExec, &rTaskData, topData, runTagsVec = std::vector(runTags)] + // runTagsVec gets copied but who cares lol + rActiveApp.set_on_draw( [&rTags, &rTasks, &rExec, &rTaskData, topData, runTagsVec = std::move(runTagsVec)] (ActiveApplication& rApp, float delta) { // Magnum Application's main loop is here @@ -289,7 +289,9 @@ static ScenarioMap_t make_scenarios() Builder_t builder{rTags, mainView.m_rTasks, mainView.m_rTaskData}; sceneOut.resize(13); - auto & [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(sceneOut); + auto & + [ + scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(sceneOut); // Compose together lots of Sessions scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, pkg); @@ -318,15 +320,18 @@ static ScenarioMap_t make_scenarios() auto const& [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(scene); - rendererOut.resize(5); - auto & [scnRender, cameraCtrl, cameraFree, shVisual, camThrow] = unpack<5>(rendererOut); - scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); - cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); - cameraFree = setup_camera_free (builder, rTopData, rTags, magnum, scnCommon, cameraCtrl); - shVisual = setup_shader_visualizer (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); - camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); + rendererOut.resize(6); + auto & [scnRender, cameraCtrl, cameraFree, shVisual, camThrow, uniTestPlanetsRdr] = unpack<6>(rendererOut); + scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); + cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); + cameraFree = setup_camera_free (builder, rTopData, rTags, magnum, scnCommon, cameraCtrl); + shVisual = setup_shader_visualizer (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); + camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); + uniTestPlanetsRdr = setup_uni_test_planets_renderer (builder, rTopData, rTags, magnum, scnRender, scnCommon, cameraCtrl, shVisual, uniCore, uniScnFrame, uniTestPlanets); - setup_magnum_draw(mainView, magnum, scnCommon, scnRender); + OSP_SESSION_UNPACK_TAGS(uniCore, TESTAPP_UNI_CORE); + + setup_magnum_draw(mainView, magnum, scnCommon, scnRender, {tgUniTimeEvt}); }; }); diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index af0e7477..1a9f18b9 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -155,7 +155,7 @@ Session setup_shape_spawn( shapeSpawn.task() = rBuilder.task().assign({tgSceneEvt, tgSpawnReq, tgSpawnEntReq, tgMeshMod, tgDrawMod, tgMatMod}).data( "Add mesh and material to spawned shapes", - TopDataIds_t{ idDrawing, idSpawner, idSpawnerEnts, idNMesh, idMatEnts, idMatDirty, idActiveIds}, + TopDataIds_t{ idDrawing, idSpawner, idSpawnerEnts, idNMesh, idMatEnts, idMatDirty, idActiveIds}, wrap_args([] (ACtxDrawing& rDrawing, SpawnerVec_t& rSpawner, EntVector_t& rSpawnerEnts, NamedMeshes& rNMesh, EntSet_t& rMatEnts, EntVector_t& rMatDirty, ActiveReg_t const& rActiveIds ) noexcept { if (rSpawner.size() == 0) diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index d7d2ed27..18322c4c 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -23,26 +23,38 @@ * SOFTWARE. */ #include "scene_renderer.h" +#include "scene_common.h" #include "scenarios.h" #include "identifiers.h" +#include "CameraController.h" + #include "../ActiveApplication.h" #include +#include #include #include #include #include #include #include +#include +#include #include +// for the 0xrrggbb_rgbf and angle literals +using namespace Magnum::Math::Literals; + using namespace osp; using namespace osp::shader; using namespace osp::active; +using namespace osp::universe; using osp::input::UserInputHandler; +using Magnum::GL::Mesh; + namespace testapp::scenes { @@ -114,6 +126,8 @@ Session setup_scene_renderer( rBuilder.tag(tgCameraReq) .depend_on({tgCameraMod}); rBuilder.tag(tgGroupFwdMod) .depend_on({tgGroupFwdDel}); rBuilder.tag(tgGroupFwdReq) .depend_on({tgGroupFwdDel, tgGroupFwdMod}); + rBuilder.tag(tgBindFboReq) .depend_on({tgBindFboMod}); + rBuilder.tag(tgFwdRenderReq) .depend_on({tgFwdRenderMod}); rBuilder.tag(tgDrawTransformNew) .depend_on({tgDrawTransformDel}); rBuilder.tag(tgDrawTransformMod) .depend_on({tgDrawTransformDel, tgDrawTransformNew}); rBuilder.tag(tgDrawTransformReq) .depend_on({tgDrawTransformDel, tgDrawTransformNew, tgDrawTransformMod}); @@ -146,34 +160,42 @@ Session setup_scene_renderer( // TODO: Separate forward renderer into different tasks to allow other // rendering techniques - renderer.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgDrawTransformReq, tgGroupFwdReq, tgDrawReq, tgCameraReq, tgEntTexMod, tgEntMeshMod}).data( - "Render and display scene", + renderer.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboMod}).data( + "Bind Offscreen FBO", TopDataIds_t{ idDrawing, idRenderGl, idGroupFwd, idCamera}, wrap_args([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept { using Magnum::GL::Framebuffer; using Magnum::GL::FramebufferClear; - using Magnum::GL::Texture2D; - // Bind offscreen FBO Framebuffer &rFbo = rRenderGl.m_fbo; rFbo.bind(); // Clear it - rFbo.clear( FramebufferClear::Color | FramebufferClear::Depth + rFbo.clear( FramebufferClear::Color | FramebufferClear::Depth | FramebufferClear::Stencil); + })); + renderer.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderReq}).data( + "Display Offscreen FBO", + TopDataIds_t{ idDrawing, idRenderGl, idGroupFwd, idCamera}, + wrap_args([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept + { + Magnum::GL::Texture2D &rFboColor = rRenderGl.m_texGl.get(rRenderGl.m_fboColor); + SysRenderGL::display_texture(rRenderGl, rFboColor); + })); + renderer.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderMod, tgDrawTransformReq, tgGroupFwdReq, tgDrawReq, tgCameraReq, tgEntTexMod, tgEntMeshMod}).data( + "Render Entities", + TopDataIds_t{ idDrawing, idRenderGl, idGroupFwd, idCamera}, + wrap_args([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept + { ViewProjMatrix viewProj{rCamera.m_transform.inverted(), rCamera.perspective()}; // Forward Render fwd_opaque group to FBO SysRenderGL::render_opaque(rGroupFwd, rDrawing.m_visible, viewProj); - - Texture2D &rFboColor = rRenderGl.m_texGl.get(rRenderGl.m_fboColor); - SysRenderGL::display_texture(rRenderGl, rFboColor); })); - renderer.task() = rBuilder.task().assign({tgSyncEvt, tgDelTotalReq, tgDrawGlDel}).data( "Delete GL components", TopDataIds_t{ idScnRender, idDelTotal}, @@ -221,22 +243,22 @@ Session setup_shader_visualizer( auto &rScnRender = top_get< ACtxSceneRenderGL > (topData, idScnRender); auto &rRenderGl = top_get< RenderGL > (topData, idRenderGl); - Session visualizer; - OSP_SESSION_ACQUIRE_DATA(visualizer, topData, TESTAPP_SHADER_VISUALIZER) - auto &rDrawVisual = top_emplace< ACtxDrawMeshVisualizer >(topData, idDrawVisual); + Session shVisual; + OSP_SESSION_ACQUIRE_DATA(shVisual, topData, TESTAPP_SHADER_VISUALIZER) + auto &rDrawVisual = top_emplace< ACtxDrawMeshVisualizer >(topData, idDrawShVisual); rDrawVisual.m_shader = MeshVisualizer{ MeshVisualizer::Configuration{}.setFlags(MeshVisualizer::Flag::Wireframe) }; rDrawVisual.assign_pointers(rScnRender, rRenderGl); - visualizer.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgGroupFwdMod}).data( + shVisual.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgGroupFwdMod}).data( "Sync MeshVisualizer shader entities", - TopDataIds_t{ idMatDirty, idMatEnts, idGroupFwd, idDrawVisual}, - wrap_args([] (EntVector_t const& rMatDirty, EntSet_t const& rMatEnts, RenderGroup& rGroup, ACtxDrawMeshVisualizer& rVisualizer) noexcept + TopDataIds_t{ idMatDirty, idMatEnts, idGroupFwd, idDrawShVisual}, + wrap_args([] (EntVector_t const& rMatDirty, EntSet_t const& rMatEnts, RenderGroup& rGroup, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept { - sync_visualizer(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, rGroup.m_entities, rVisualizer); + sync_visualizer(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, rGroup.m_entities, rDrawShVisual); })); - visualizer.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgHierReq, tgTransformReq, tgDrawTransformNew}).data( + shVisual.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgTransformReq, tgDrawTransformNew}).data( "Add draw transforms to mesh visualizer", TopDataIds_t{ idMatDirty, idScnRender}, wrap_args([] (EntVector_t const& rMatDirty, ACtxSceneRenderGL& rScnRender) noexcept @@ -245,24 +267,229 @@ Session setup_shader_visualizer( })); - return visualizer; + return shVisual; } -// TODO -#if 0 +Session setup_shader_flat( + Builder_t& rBuilder, + ArrayView const topData, + Tags& rTags, + Session const& magnum, + Session const& scnCommon, + Session const& scnRender, + Session const& material) +{ + using osp::shader::Flat; + + OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); + OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); + OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); + auto &rScnRender = top_get< ACtxSceneRenderGL > (topData, idScnRender); + auto &rRenderGl = top_get< RenderGL > (topData, idRenderGl); + + Session shFlat; + OSP_SESSION_ACQUIRE_DATA(shFlat, topData, TESTAPP_SHADER_FLAT) + auto &rDrawFlat = top_emplace< ACtxDrawFlat >(topData, idDrawShFlat); + + rDrawFlat.m_shaderDiffuse = Flat{Flat::Configuration{}.setFlags(Flat::Flag::Textured)}; + rDrawFlat.m_shaderUntextured = Flat{Flat::Configuration{}}; + rDrawFlat.assign_pointers(rScnRender, rRenderGl); + + shFlat.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, tgTexGlReq, tgGroupFwdMod}).data( + "Sync Flat shader entities", + TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShFlat}, + wrap_args([] (EntVector_t const& rMatDirty, EntSet_t const& rMatEnts, ACtxDrawing const& rDrawing, ACtxSceneRenderGL const& rScnRender, RenderGroup& rGroupFwd, ACtxDrawFlat& rDrawShFlat) noexcept + { + sync_flat(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, &rGroupFwd.m_entities, nullptr, rDrawing.m_opaque, rScnRender.m_diffuseTexId, rDrawShFlat); + })); + + shFlat.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgTransformReq, tgDrawTransformNew}).data( + "Add draw transforms to entities with Phong shader", + TopDataIds_t{ idMatDirty, idScnRender}, + wrap_args([] (EntVector_t const& rMatDirty, ACtxSceneRenderGL& rScnRender) noexcept + { + SysRender::assure_draw_transforms(rScnRender.m_drawTransform, std::cbegin(rMatDirty), std::cend(rMatDirty)); + })); + + return shFlat; +} + -Session setup_shader_phong() +Session setup_shader_phong( + Builder_t& rBuilder, + ArrayView const topData, + Tags& rTags, + Session const& magnum, + Session const& scnCommon, + Session const& scnRender, + Session const& material) { - // Setup Phong shaders + using osp::shader::Phong; + + OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); + OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); + OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); + auto &rScnRender = top_get< ACtxSceneRenderGL > (topData, idScnRender); + auto &rRenderGl = top_get< RenderGL > (topData, idRenderGl); + + Session shPhong; + OSP_SESSION_ACQUIRE_DATA(shPhong, topData, TESTAPP_SHADER_PHONG) + auto &rDrawPhong = top_emplace< ACtxDrawPhong >(topData, idDrawShPhong); + + auto const texturedFlags = Phong::Flag::DiffuseTexture | Phong::Flag::AlphaMask | Phong::Flag::AmbientTexture; - rDrawPhong.m_shaderDiffuse = Phong{texturedFlags, 2}; - rDrawPhong.m_shaderUntextured = Phong{{}, 2}; + rDrawPhong.m_shaderDiffuse = Phong{Phong::Configuration{}.setFlags(texturedFlags).setLightCount(2)}; + rDrawPhong.m_shaderUntextured = Phong{Phong::Configuration{}.setLightCount(2)}; rDrawPhong.assign_pointers(rScnRender, rRenderGl); + shPhong.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, tgTexGlReq, tgGroupFwdMod}).data( + "Sync Phong shader entities", + TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShPhong}, + wrap_args([] (EntVector_t const& rMatDirty, EntSet_t const& rMatEnts, ACtxDrawing const& rDrawing, ACtxSceneRenderGL const& rScnRender, RenderGroup& rGroupFwd, ACtxDrawPhong& rDrawShPhong) noexcept + { + sync_phong(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, &rGroupFwd.m_entities, nullptr, rDrawing.m_opaque, rScnRender.m_diffuseTexId, rDrawShPhong); + })); + + shPhong.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgTransformReq, tgDrawTransformNew}).data( + "Add draw transforms to entities with Phong shader", + TopDataIds_t{ idMatDirty, idScnRender}, + wrap_args([] (EntVector_t const& rMatDirty, ACtxSceneRenderGL& rScnRender) noexcept + { + SysRender::assure_draw_transforms(rScnRender.m_drawTransform, std::cbegin(rMatDirty), std::cend(rMatDirty)); + })); + + return shPhong; } -#endif +Session setup_uni_test_planets_renderer( + Builder_t& rBuilder, + ArrayView const topData, + Tags& rTags, + Session const& magnum, + Session const& scnRender, + Session const& scnCommon, + Session const& cameraCtrl, + Session const& visualizer, + Session const& uniCore, + Session const& uniScnFrame, + Session const& uniTestPlanets) +{ + Session uniTestPlanetsRdr; + + OSP_SESSION_UNPACK_TAGS(magnum, TESTAPP_APP_MAGNUM); + OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); + OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(cameraCtrl, TESTAPP_CAMERA_CTRL); + OSP_SESSION_UNPACK_TAGS(cameraCtrl, TESTAPP_CAMERA_CTRL); + OSP_SESSION_UNPACK_DATA(visualizer, TESTAPP_SHADER_VISUALIZER); + OSP_SESSION_UNPACK_TAGS(uniCore, TESTAPP_UNI_CORE); + OSP_SESSION_UNPACK_DATA(uniCore, TESTAPP_UNI_CORE); + OSP_SESSION_UNPACK_TAGS(uniScnFrame, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_UNPACK_DATA(uniScnFrame, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_UNPACK_DATA(uniTestPlanets, TESTAPP_UNI_PLANETS); + + //OSP_SESSION_ACQUIRE_DATA(uniTestPlanets, topData, TESTAPP_UNI_PLANETS); + + uniTestPlanetsRdr.task() = rBuilder.task().assign({tgRenderEvt, tgCamCtrlReq}).data( + "Position SceneFrame center Camera Controller", + TopDataIds_t{ idCamCtrl, idScnFrame}, + wrap_args([] (ACtxCameraController const& rCamCtrl, SceneFrame& rScnFrame) noexcept + { + rScnFrame.m_scenePosition = Vector3g(math::mul_2pow( rCamCtrl.m_target.value(), rScnFrame.m_precision)); + })); + + uniTestPlanetsRdr.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderMod, tgDrawReq, tgCameraReq}).data( + "Render test planets", + TopDataIds_t{ idRenderGl, idCamera, idDrawShVisual, idDrawingRes, idNMesh, idUniverse, idScnFrame, idPlanetMainSpace}, + wrap_args([] (RenderGL& rRenderGl, Camera const& rCamera, ACtxDrawMeshVisualizer& rDrawShVisual, ACtxDrawingRes const& rDrawingRes, NamedMeshes& rNMesh, Universe& rUniverse, SceneFrame const& rScnFrame, CoSpaceId const planetMainSpace) noexcept + { + CoSpaceCommon &rMainSpace = rUniverse.m_coordCommon[planetMainSpace]; + auto const [x, y, z] = sat_views(rMainSpace.m_satPositions, rMainSpace.m_data, rMainSpace.m_satCount); + auto const [qx, qy, qz, qw] = sat_views(rMainSpace.m_satRotations, rMainSpace.m_data, rMainSpace.m_satCount); + + // Calculate transform from universe to area/local-space for rendering. + // This can be generalized by finding a common ancestor within the tree + // of coordinate spaces. Since there's only two possibilities, an if + // statement works. + CoordTransformer mainToArea; + if (rScnFrame.m_parent == planetMainSpace) + { + mainToArea = coord_parent_to_child(rMainSpace, rScnFrame); + } + else + { + CoSpaceId const landedId = rScnFrame.m_parent; + CoSpaceCommon &rLanded = rUniverse.m_coordCommon[landedId]; + + CoSpaceTransform const landedTf = coord_get_transform(rLanded, rLanded, x, y, z, qx, qy, qz, qw); + CoordTransformer const mainToLanded = coord_parent_to_child(rMainSpace, landedTf); + CoordTransformer const landedToArea = coord_parent_to_child(landedTf, rScnFrame); + + mainToArea = coord_composite(landedToArea, mainToLanded); + } + Quaternion const mainToAreaRot{mainToArea.rotation()}; + + float const scale = math::mul_2pow(1.0f, -rMainSpace.m_precision); + + ViewProjMatrix viewProj{rCamera.m_transform.inverted(), rCamera.perspective()}; + + MeshId const sphereMeshId = rNMesh.m_shapeToMesh[phys::EShape::Sphere]; + ResId const sphereResId = rDrawingRes.m_meshToRes.at(sphereMeshId); + MeshGlId const sphereMeshGlId = rRenderGl.m_resToMesh.at(sphereResId); + Mesh& rSphereMeshGl = rRenderGl.m_meshGl.get(sphereMeshGlId); + + using Magnum::GL::Renderer; + Renderer::enable(Renderer::Feature::DepthTest); + Renderer::enable(Renderer::Feature::FaceCulling); + Renderer::disable(Renderer::Feature::Blending); + //Renderer::setDepthMask(true); + + // Draw black hole + Vector3 const blackHolePos = Vector3(mainToArea.transform_position({})) * scale; + rDrawShVisual.m_shader + .setColor(0x0E0E0E_rgbf) + .setTransformationMatrix( + viewProj.m_view + * Matrix4::translation(blackHolePos) + * Matrix4::scaling({200, 200, 200}) + * Matrix4{mainToAreaRot.toMatrix()} ) + .draw(rSphereMeshGl); + + // Draw planets + rDrawShVisual.m_shader.setColor(0xFFFFFF_rgbf); + for (std::size_t i = 0; i < rMainSpace.m_satCount; ++i) + { + Vector3g const relative = mainToArea.transform_position({x[i], y[i], z[i]}); + Vector3 const relativeMeters = Vector3(relative) * scale; + + Quaterniond const rot{{qx[i], qy[i], qz[i]}, qw[i]}; + + rDrawShVisual.m_shader + .setColor(0xFFFFFF_rgbf) + .setTransformationMatrix( + viewProj.m_view + * Matrix4::translation(relativeMeters) + * Matrix4::scaling({200, 200, 200}) + * Matrix4{(mainToAreaRot * Quaternion{rot}).toMatrix()} ) + .draw(rSphereMeshGl); + } + + })); + + return uniTestPlanetsRdr; } +} // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index b724d92a..11b072de 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -63,4 +63,38 @@ osp::Session setup_shader_visualizer( osp::Session const& scnRender, osp::Session const& material); + +osp::Session setup_shader_flat( + Builder_t& rBuilder, + osp::ArrayView topData, + osp::Tags& rTags, + osp::Session const& magnum, + osp::Session const& scene, + osp::Session const& scnRender, + osp::Session const& material); + + +osp::Session setup_shader_phong( + Builder_t& rBuilder, + osp::ArrayView topData, + osp::Tags& rTags, + osp::Session const& magnum, + osp::Session const& scene, + osp::Session const& scnRender, + osp::Session const& material); + + +osp::Session setup_uni_test_planets_renderer( + Builder_t& rBuilder, + osp::ArrayView topData, + osp::Tags& rTags, + osp::Session const& magnum, + osp::Session const& scnRender, + osp::Session const& scnCommon, + osp::Session const& cameraCtrl, + osp::Session const& visualizer, + osp::Session const& uniCore, + osp::Session const& uniScnFrame, + osp::Session const& uniTestPlanets); + } diff --git a/src/test_application/activescenes/scene_universe.cpp b/src/test_application/activescenes/scene_universe.cpp index 8673dbc6..ee4c3ebd 100644 --- a/src/test_application/activescenes/scene_universe.cpp +++ b/src/test_application/activescenes/scene_universe.cpp @@ -27,8 +27,12 @@ #include "identifiers.h" +#include #include +#include +#include + #include using namespace osp; @@ -57,13 +61,15 @@ Session setup_uni_sceneframe( ArrayView topData, Tags& rTags) { - Session uniScnFrame; - OSP_SESSION_ACQUIRE_DATA(uniScnFrame, topData, TESTAPP_UNI_SCENEFRAME); - OSP_SESSION_ACQUIRE_TAGS(uniScnFrame, rTags, TESTAPP_UNI_SCENEFRAME); + Session ScnFrame; + OSP_SESSION_ACQUIRE_DATA(ScnFrame, topData, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_ACQUIRE_TAGS(ScnFrame, rTags, TESTAPP_UNI_SCENEFRAME); + + rBuilder.tag(tgScnFramePosReq).depend_on({tgScnFramePosMod}); - top_emplace< SceneFrame > (topData, idUniScnFrame); + top_emplace< SceneFrame > (topData, idScnFrame); - return uniScnFrame; + return ScnFrame; } @@ -72,33 +78,28 @@ Session setup_uni_test_planets( ArrayView topData, Tags& rTags, Session const& uniCore, - Session const& uniScnFrame) + Session const& ScnFrame) { + using CoSpaceIdVec_t = std::vector; using Corrade::Containers::Array; OSP_SESSION_UNPACK_TAGS(uniCore, TESTAPP_UNI_CORE); OSP_SESSION_UNPACK_DATA(uniCore, TESTAPP_UNI_CORE); - OSP_SESSION_UNPACK_TAGS(uniScnFrame, TESTAPP_UNI_SCENEFRAME); - OSP_SESSION_UNPACK_DATA(uniScnFrame, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_UNPACK_TAGS(ScnFrame, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_UNPACK_DATA(ScnFrame, TESTAPP_UNI_SCENEFRAME); auto &rUniverse = top_get< Universe >(topData, idUniverse); - Session uniTestPlanets; - - constexpr int precision = 10; - constexpr int planetCount = 64; - constexpr int seed = 1337; - constexpr spaceint_t maxDist = 20000ul << precision; - constexpr float maxVel = 800.0f; - - constexpr std::size_t planetSize = sizeof(spaceint_t) * 3 // Position - + sizeof(double) * 3 // Velocity - + sizeof(double) * 4; // Rotation + constexpr int precision = 10; + constexpr int planetCount = 64; + constexpr int seed = 1337; + constexpr spaceint_t maxDist = math::mul_2pow(20000ul, precision); + constexpr float maxVel = 800.0f; // Create coordinate spaces CoSpaceId const mainSpace = rUniverse.m_coordIds.create(); - std::vector surfaceSpaces(planetCount); - rUniverse.m_coordIds.create(surfaceSpaces.begin(), surfaceSpaces.end()); + std::vector satSurfaceSpaces(planetCount); + rUniverse.m_coordIds.create(satSurfaceSpaces.begin(), satSurfaceSpaces.end()); rUniverse.m_coordCommon.resize(rUniverse.m_coordIds.capacity()); @@ -109,30 +110,31 @@ Session setup_uni_test_planets( // Associate each planet satellite with their surface coordinate space for (SatId satId = 0; satId < planetCount; ++satId) { - CoSpaceId const surfaceSpaceId = surfaceSpaces[satId]; + CoSpaceId const surfaceSpaceId = satSurfaceSpaces[satId]; CoSpaceCommon &rCommon = rUniverse.m_coordCommon[surfaceSpaceId]; rCommon.m_parent = mainSpace; rCommon.m_parentSat = satId; } - // Create description for Position, Velocity, and Rotation data - // TODO: Alignment is needed for SIMD. see Corrade alignedAlloc + // Coordinate space data is a single allocation partitioned to hold positions, velocities, and + // rotations. + // TODO: Alignment is needed for SIMD (not yet implemented). see Corrade alignedAlloc std::size_t bytesUsed = 0; // Positions and velocities are arranged as XXXX... YYYY... ZZZZ... - interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satPositions[0]); - interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satPositions[1]); - interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satPositions[2]); - interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satVelocities[0]); - interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satVelocities[1]); - interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satVelocities[2]); + partition(bytesUsed, planetCount, rMainSpaceCommon.m_satPositions[0]); + partition(bytesUsed, planetCount, rMainSpaceCommon.m_satPositions[1]); + partition(bytesUsed, planetCount, rMainSpaceCommon.m_satPositions[2]); + partition(bytesUsed, planetCount, rMainSpaceCommon.m_satVelocities[0]); + partition(bytesUsed, planetCount, rMainSpaceCommon.m_satVelocities[1]); + partition(bytesUsed, planetCount, rMainSpaceCommon.m_satVelocities[2]); // Rotations use XYZWXYZWXYZWXYZW... - interleave(bytesUsed, planetCount, rMainSpaceCommon.m_satRotations[0], - rMainSpaceCommon.m_satRotations[1], - rMainSpaceCommon.m_satRotations[2], - rMainSpaceCommon.m_satRotations[3]); + partition(bytesUsed, planetCount, rMainSpaceCommon.m_satRotations[0], + rMainSpaceCommon.m_satRotations[1], + rMainSpaceCommon.m_satRotations[2], + rMainSpaceCommon.m_satRotations[3]); // Allocate data for all planets rMainSpaceCommon.m_data = Array{Corrade::NoInit, bytesUsed}; @@ -148,7 +150,7 @@ Session setup_uni_test_planets( for (std::size_t i = 0; i < planetCount; ++i) { - // Assign each planet random ositions and velocities + // Assign each planet random positions and velocities x[i] = posDist(gen); y[i] = posDist(gen); z[i] = posDist(gen); @@ -163,11 +165,128 @@ Session setup_uni_test_planets( qw[i] = 1.0; } - return uniTestPlanets; -} + // Set initial scene frame -} + auto &rScnFrame = top_get(topData, idScnFrame); + rScnFrame.m_parent = mainSpace; + rScnFrame.m_position = math::mul_2pow({400, 400, 400}, precision); + + Session uniTestPlanets; + OSP_SESSION_ACQUIRE_DATA(uniTestPlanets, topData, TESTAPP_UNI_PLANETS); + top_emplace< CoSpaceId > (topData, idPlanetMainSpace, mainSpace); + top_emplace< float > (topData, tgUniDeltaTimeIn, 1.0f / 60.0f); + top_emplace< CoSpaceIdVec_t > (topData, idSatSurfaceSpaces, std::move(satSurfaceSpaces)); + uniTestPlanets.task() = rBuilder.task().assign({tgUniTimeEvt, tgScnFramePosReq}).data( + "Update planets", + TopDataIds_t{ idUniverse, idPlanetMainSpace, idScnFrame, idSatSurfaceSpaces, tgUniDeltaTimeIn}, + wrap_args([] (Universe& rUniverse, CoSpaceId const planetMainSpace, SceneFrame &rScnFrame, CoSpaceIdVec_t const& rSatSurfaceSpaces, float const uniDeltaTimeIn) noexcept + { + CoSpaceCommon &rMainSpaceCommon = rUniverse.m_coordCommon[planetMainSpace]; + + float const scale = osp::math::mul_2pow(1.0f, -rMainSpaceCommon.m_precision); + float const scaleDelta = uniDeltaTimeIn / scale; + + auto const [x, y, z] = sat_views(rMainSpaceCommon.m_satPositions, rMainSpaceCommon.m_data, rMainSpaceCommon.m_satCount); + auto const [vx, vy, vz] = sat_views(rMainSpaceCommon.m_satVelocities, rMainSpaceCommon.m_data, rMainSpaceCommon.m_satCount); + auto const [qx, qy, qz, qw] = sat_views(rMainSpaceCommon.m_satRotations, rMainSpaceCommon.m_data, rMainSpaceCommon.m_satCount); + + // Phase 1: Move satellites + + for (std::size_t i = 0; i < rMainSpaceCommon.m_satCount; ++i) + { + x[i] += vx[i] * scaleDelta; + y[i] += vy[i] * scaleDelta; + z[i] += vz[i] * scaleDelta; + + // Apply arbitrary inverse-square gravity towards origin + Vector3d const pos = Vector3d( Vector3g( x[i], y[i], z[i] ) ) * scale; + float const r = pos.length(); + float const c_gm = 10000000000.0f; + Vector3d const accel = -pos * uniDeltaTimeIn * c_gm / (r * r * r); + + vx[i] += accel.x(); + vy[i] += accel.y(); + vz[i] += accel.z(); + + // Rotate based on i, semi-random + Vector3d const axis = Vector3d{std::sin(i), std::cos(i), double(i % 8 - 4)}.normalized(); + Radd const speed{(i % 16) / 16.0}; + + Quaterniond const rot = Quaterniond{{qx[i], qy[i], qz[i]}, qw[i]} + * Quaterniond::rotation(speed * uniDeltaTimeIn, axis); + qx[i] = rot.vector().x(); + qy[i] = rot.vector().y(); + qz[i] = rot.vector().z(); + qw[i] = rot.scalar(); + } + + // Phase 2: Transfers and stuff + + constexpr float captureDist = 500.0f; + + Vector3g const cameraPos{rScnFrame.m_rotation.transformVector(Vector3d(rScnFrame.m_scenePosition))}; + Vector3g const areaPos{rScnFrame.m_position + cameraPos}; + + bool const notInPlanet = (rScnFrame.m_parent == planetMainSpace); + + if (notInPlanet) + { + // Find a planet to enter + std::size_t nearbyPlanet = rMainSpaceCommon.m_satCount; + for (std::size_t i = 0; i < rMainSpaceCommon.m_satCount; ++i) + { + Vector3 const diff = (Vector3( x[i], y[i], z[i] ) - Vector3(areaPos)) * scale; + if (diff.length() < captureDist) + { + nearbyPlanet = i; + break; + } + } + + if (nearbyPlanet < rMainSpaceCommon.m_satCount) + { + OSP_LOG_INFO("Captured into Satellite {} under CoordSpace {}", + nearbyPlanet, int(rSatSurfaceSpaces[nearbyPlanet])); + + CoSpaceId const surface = rSatSurfaceSpaces[nearbyPlanet]; + CoSpaceCommon &rSurfaceCommon = rUniverse.m_coordCommon[surface]; + + CoSpaceTransform const surfaceTf = coord_get_transform(rSurfaceCommon, rSurfaceCommon, x, y, z, qx, qy, qz, qw); + CoordTransformer const mainToSurface = coord_parent_to_child(rMainSpaceCommon, surfaceTf); + + // Transfer scene frame from Main to Surface coordinate space + rScnFrame.m_parent = surface; + rScnFrame.m_position = mainToSurface.transform_position(rScnFrame.m_position); + rScnFrame.m_rotation = mainToSurface.rotation() * rScnFrame.m_rotation; + } + } + else + { + // Currently within planet, try to escape it + + Vector3 const diff = Vector3(areaPos) * scale; + if (diff.length() > captureDist) + { + OSP_LOG_INFO("Leaving planet"); + + CoSpaceId const surface = rScnFrame.m_parent; + CoSpaceCommon &rSurfaceCommon = rUniverse.m_coordCommon[surface]; + + CoSpaceTransform const surfaceTf = coord_get_transform(rSurfaceCommon, rSurfaceCommon, x, y, z, qx, qy, qz, qw); + CoordTransformer const surfaceToMain = coord_child_to_parent(rMainSpaceCommon, surfaceTf); + + // Transfer scene frame from Surface to Main coordinate space + rScnFrame.m_parent = planetMainSpace; + rScnFrame.m_position = surfaceToMain.transform_position(rScnFrame.m_position); + rScnFrame.m_rotation = surfaceToMain.rotation() * rScnFrame.m_rotation; + } + } + })); + + return uniTestPlanets; +} +} // namespace testapp::scenes diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index 45f6f1df..b41103c6 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -413,6 +413,7 @@ void load_a_bunch_of_stuff() Trade::MeshData &&cylinder = Magnum::MeshTools::transform3D( Primitives::cylinderSolid(3, 16, 1.0f, CylinderFlag::CapEnds), Matrix4::rotationX(Deg(90)), 0); add_mesh_quick("cube", Primitives::cubeSolid()); + add_mesh_quick("cubewire", Primitives::cubeWireframe()); add_mesh_quick("sphere", Primitives::icosphereSolid(2)); add_mesh_quick("cylinder", std::move(cylinder)); add_mesh_quick("grid64solid", Primitives::grid3DSolid({63, 63})); From 303816f226dc14c4f1f8ccd3050edf77f2b9de08 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 5 Mar 2023 19:17:16 -0800 Subject: [PATCH 03/35] Add cursor and some drawing-related changes --- src/osp/Active/SysRender.cpp | 10 ++ src/osp/Active/SysRender.h | 14 +++ .../activescenes/identifiers.h | 5 + .../activescenes/scenarios.cpp | 30 ++--- src/test_application/activescenes/scenarios.h | 3 +- .../activescenes/scene_common.cpp | 10 +- .../activescenes/scene_misc.cpp | 10 +- .../activescenes/scene_renderer.cpp | 106 ++++++++++++++++-- .../activescenes/scene_renderer.h | 11 ++ src/test_application/main.cpp | 5 +- 10 files changed, 162 insertions(+), 42 deletions(-) diff --git a/src/osp/Active/SysRender.cpp b/src/osp/Active/SysRender.cpp index f541f8b1..dd0f6a28 100644 --- a/src/osp/Active/SysRender.cpp +++ b/src/osp/Active/SysRender.cpp @@ -135,3 +135,13 @@ void SysRender::update_draw_transforms_recurse( //parentTf. } + +MeshIdOwner_t SysRender::add_drawable_mesh(ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources, PkgId const pkg, std::string_view const name) +{ + ResId const res = rResources.find(restypes::gc_mesh, pkg, name); + assert(res != lgrn::id_null()); + MeshId const meshId = SysRender::own_mesh_resource(rDrawing, rDrawingRes, rResources, res); + return rDrawing.m_meshRefCounts.ref_add(meshId); +} + + diff --git a/src/osp/Active/SysRender.h b/src/osp/Active/SysRender.h index 28e5f058..86b22ef2 100644 --- a/src/osp/Active/SysRender.h +++ b/src/osp/Active/SysRender.h @@ -201,6 +201,10 @@ class SysRender static void update_delete_groups( ACtxRenderGroups& rCtxGroups, IT_T first, IT_T const& last); + static MeshIdOwner_t add_drawable_mesh(ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources, PkgId const pkg, std::string_view const name); + + static constexpr decltype(auto) gen_drawable_mesh_adder(ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources, PkgId const pkg); + private: static void update_draw_transforms_recurse( ACtxSceneGraph const& rScnGraph, @@ -296,4 +300,14 @@ void SysRender::update_delete_groups( } } +constexpr decltype(auto) SysRender::gen_drawable_mesh_adder(ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources, PkgId const pkg) +{ + return [&rDrawing, &rDrawingRes, &rResources, pkg] (std::string_view const name) -> MeshIdOwner_t + { + return add_drawable_mesh(rDrawing, rDrawingRes, rResources, pkg, name); + }; +} + + + } // namespace osp::active diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index a18680a1..cab8c85d 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -262,6 +262,11 @@ +#define OSP_DATA_TESTAPP_CURSOR 1, \ + idCursorData + + + #define OSP_DATA_TESTAPP_VEHICLE_CONTROL 1, \ idVhControls #define OSP_TAGS_TESTAPP_VEHICLE_CONTROL 2, \ diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 3889830d..079cfa8d 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -108,7 +108,7 @@ static ScenarioMap_t make_scenarios() }; add_scenario("enginetest", "Basic game engine and drawing scenario (without using TopTasks)", - [] (MainView mainView, PkgId pkg, Sessions_t& sceneOut) -> RendererSetup_t + [] (MainView mainView, Sessions_t& sceneOut) -> RendererSetup_t { sceneOut.resize(1); TopDataId const idSceneData = sceneOut.front().acquire_data<1>(mainView.m_topData).front(); @@ -116,7 +116,7 @@ static ScenarioMap_t make_scenarios() // enginetest::setup_scene returns an entt::any containing one big // struct containing all the scene data. - top_assign(mainView.m_topData, idSceneData, enginetest::setup_scene(rResources, pkg)); + top_assign(mainView.m_topData, idSceneData, enginetest::setup_scene(rResources, mainView.m_defaultPkg)); return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, [[maybe_unused]] Sessions_t& rendererOut) { @@ -134,7 +134,7 @@ static ScenarioMap_t make_scenarios() }); add_scenario("physics", "Newton Dynamics integration test scenario", - [] (MainView mainView, PkgId pkg, Sessions_t& sceneOut) -> RendererSetup_t + [] (MainView mainView, Sessions_t& sceneOut) -> RendererSetup_t { using namespace testapp::scenes; @@ -147,7 +147,7 @@ static ScenarioMap_t make_scenarios() auto & [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt] = unpack<10>(sceneOut); // Compose together lots of Sessions - scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, pkg); + scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); matVisual = setup_material (builder, rTopData, rTags, scnCommon); physics = setup_physics (builder, rTopData, rTags, scnCommon); shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, scnCommon, physics, matVisual); @@ -159,7 +159,7 @@ static ScenarioMap_t make_scenarios() nwtGrav = setup_newton_force_accel (builder, rTopData, rTags, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, rTags, scnCommon, physics, shapeSpawn, newton, nwtGravSet); - add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, pkg); + add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, mainView.m_defaultPkg); return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, [[maybe_unused]] Sessions_t& rendererOut) { @@ -182,7 +182,7 @@ static ScenarioMap_t make_scenarios() }); add_scenario("vehicles", "Physics scenario but with Vehicles", - [] (MainView mainView, PkgId pkg, Sessions_t& sceneOut) -> RendererSetup_t + [] (MainView mainView, Sessions_t& sceneOut) -> RendererSetup_t { using namespace testapp::scenes; using namespace osp::active; @@ -203,7 +203,7 @@ static ScenarioMap_t make_scenarios() newton, nwtGravSet, nwtGrav, shapeSpawnNwt, vehicleSpawnNwt, nwtRocketSet, rocketsNwt ] = unpack<24>(sceneOut); - scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, pkg); + scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); matVisual = setup_material (builder, rTopData, rTags, scnCommon); physics = setup_physics (builder, rTopData, rTags, scnCommon); shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, scnCommon, physics, matVisual); @@ -231,7 +231,7 @@ static ScenarioMap_t make_scenarios() OSP_SESSION_UNPACK_DATA(vehicleSpawnVB, TESTAPP_VEHICLE_SPAWN_VB); OSP_SESSION_UNPACK_DATA(testVehicles, TESTAPP_TEST_VEHICLES); - add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, pkg); + add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, mainView.m_defaultPkg); auto &rActiveIds = top_get (rTopData, idActiveIds); auto &rTVPartVehicle = top_get (rTopData, idTVPartVehicle); @@ -279,7 +279,7 @@ static ScenarioMap_t make_scenarios() }); add_scenario("universe", "Universe test scenario with very unrealistic planets", - [] (MainView mainView, PkgId pkg, Sessions_t& sceneOut) -> RendererSetup_t + [] (MainView mainView, Sessions_t& sceneOut) -> RendererSetup_t { using namespace testapp::scenes; @@ -294,7 +294,7 @@ static ScenarioMap_t make_scenarios() scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(sceneOut); // Compose together lots of Sessions - scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, pkg); + scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); matVisual = setup_material (builder, rTopData, rTags, scnCommon); physics = setup_physics (builder, rTopData, rTags, scnCommon); shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, scnCommon, physics, matVisual); @@ -310,9 +310,9 @@ static ScenarioMap_t make_scenarios() uniScnFrame = setup_uni_sceneframe (builder, rTopData, rTags); uniTestPlanets = setup_uni_test_planets (builder, rTopData, rTags, uniCore, uniScnFrame); - add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, pkg); + add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, mainView.m_defaultPkg); - return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, [[maybe_unused]] Sessions_t& rendererOut) + return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, Sessions_t& rendererOut) { auto &rTopData = mainView.m_topData; auto &rTags = mainView.m_rTags; @@ -320,13 +320,15 @@ static ScenarioMap_t make_scenarios() auto const& [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(scene); - rendererOut.resize(6); - auto & [scnRender, cameraCtrl, cameraFree, shVisual, camThrow, uniTestPlanetsRdr] = unpack<6>(rendererOut); + rendererOut.resize(8); + auto & [scnRender, cameraCtrl, cameraFree, shFlat, shVisual, camThrow, cursor, uniTestPlanetsRdr] = unpack<8>(rendererOut); scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); cameraFree = setup_camera_free (builder, rTopData, rTags, magnum, scnCommon, cameraCtrl); + shFlat = setup_shader_flat (builder, rTopData, rTags, magnum, scnCommon, scnRender, {}); shVisual = setup_shader_visualizer (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); + cursor = setup_cursor (builder, rTopData, rTags, magnum, scnCommon, scnRender, cameraCtrl, shFlat, mainView.m_idResources, mainView.m_defaultPkg); uniTestPlanetsRdr = setup_uni_test_planets_renderer (builder, rTopData, rTags, magnum, scnRender, scnCommon, cameraCtrl, shVisual, uniCore, uniScnFrame, uniTestPlanets); OSP_SESSION_UNPACK_TAGS(uniCore, TESTAPP_UNI_CORE); diff --git a/src/test_application/activescenes/scenarios.h b/src/test_application/activescenes/scenarios.h index 611cb111..7f565795 100644 --- a/src/test_application/activescenes/scenarios.h +++ b/src/test_application/activescenes/scenarios.h @@ -52,12 +52,13 @@ struct MainView osp::ExecutionContext & m_rExec; osp::TopTaskDataVec_t & m_rTaskData; osp::TopDataId m_idResources; + osp::PkgId m_defaultPkg; }; using Builder_t = osp::TaskBuilder; using RendererSetup_t = void(*)(MainView, osp::Session const&, osp::Sessions_t const&, osp::Sessions_t&); -using SceneSetup_t = RendererSetup_t(*)(MainView, osp::PkgId, osp::Sessions_t&); +using SceneSetup_t = RendererSetup_t(*)(MainView, osp::Sessions_t&); struct ScenarioOption { diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index c5565d26..cf90e467 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -173,14 +173,8 @@ Session setup_common_scene( })); - // Convenient function to get a reference-counted mesh owner - auto const quick_add_mesh = [&rResources, &rDrawing, &rDrawingRes, pkg] (std::string_view const name) -> MeshIdOwner_t - { - osp::ResId const res = rResources.find(osp::restypes::gc_mesh, pkg, name); - assert(res != lgrn::id_null()); - MeshId const meshId = SysRender::own_mesh_resource(rDrawing, rDrawingRes, rResources, res); - return rDrawing.m_meshRefCounts.ref_add(meshId); - }; + // Convenient functor to get a reference-counted mesh owner + auto const quick_add_mesh = SysRender::gen_drawable_mesh_adder(rDrawing, rDrawingRes, rResources, pkg); scnCommon.task() = rBuilder.task().assign({tgCleanupEvt}).data( "Clean up NamedMeshes mesh and texture owners", diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index 47d0f820..5e66f59f 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -72,14 +72,8 @@ void add_floor( auto &rMatDirty = top_get< EntVector_t > (topData, idMatDirty); auto &rSpawner = top_get< SpawnerVec_t > (topData, idSpawner); - // Convenient function to get a reference-counted mesh owner - auto const quick_add_mesh = [&rResources, &rDrawing, &rDrawingRes, pkg] (std::string_view const name) -> MeshIdOwner_t - { - osp::ResId const res = rResources.find(osp::restypes::gc_mesh, pkg, name); - assert(res != lgrn::id_null()); - MeshId const meshId = SysRender::own_mesh_resource(rDrawing, rDrawingRes, rResources, res); - return rDrawing.m_meshRefCounts.ref_add(meshId); - }; + // Convenient functor to get a reference-counted mesh owner + auto const quick_add_mesh = SysRender::gen_drawable_mesh_adder(rDrawing, rDrawingRes, rResources, pkg); // start making floor diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 18322c4c..cf7350d6 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -238,8 +238,6 @@ Session setup_shader_visualizer( OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); - OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); auto &rScnRender = top_get< ACtxSceneRenderGL > (topData, idScnRender); auto &rRenderGl = top_get< RenderGL > (topData, idRenderGl); @@ -250,6 +248,17 @@ Session setup_shader_visualizer( rDrawVisual.m_shader = MeshVisualizer{ MeshVisualizer::Configuration{}.setFlags(MeshVisualizer::Flag::Wireframe) }; rDrawVisual.assign_pointers(rScnRender, rRenderGl); + // Default colors + rDrawVisual.m_shader.setWireframeColor({0.7f, 0.5f, 0.7f, 1.0f}); + rDrawVisual.m_shader.setColor({0.2f, 0.1f, 0.5f, 1.0f}); + + if (material.m_dataIds.empty()) + { + return shVisual; + } + OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); + OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); + shVisual.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgGroupFwdMod}).data( "Sync MeshVisualizer shader entities", TopDataIds_t{ idMatDirty, idMatEnts, idGroupFwd, idDrawShVisual}, @@ -287,8 +296,6 @@ Session setup_shader_flat( OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); - OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); auto &rScnRender = top_get< ACtxSceneRenderGL > (topData, idScnRender); auto &rRenderGl = top_get< RenderGL > (topData, idRenderGl); @@ -300,6 +307,13 @@ Session setup_shader_flat( rDrawFlat.m_shaderUntextured = Flat{Flat::Configuration{}}; rDrawFlat.assign_pointers(rScnRender, rRenderGl); + if (material.m_dataIds.empty()) + { + return shFlat; + } + OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); + OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); + shFlat.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, tgTexGlReq, tgGroupFwdMod}).data( "Sync Flat shader entities", TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShFlat}, @@ -336,8 +350,6 @@ Session setup_shader_phong( OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); - OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); auto &rScnRender = top_get< ACtxSceneRenderGL > (topData, idScnRender); auto &rRenderGl = top_get< RenderGL > (topData, idRenderGl); @@ -351,6 +363,13 @@ Session setup_shader_phong( rDrawPhong.m_shaderUntextured = Phong{Phong::Configuration{}.setLightCount(2)}; rDrawPhong.assign_pointers(rScnRender, rRenderGl); + if (material.m_dataIds.empty()) + { + return shPhong; + } + OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); + OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); + shPhong.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, tgTexGlReq, tgGroupFwdMod}).data( "Sync Phong shader entities", TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShPhong}, @@ -370,6 +389,78 @@ Session setup_shader_phong( return shPhong; } +struct Cursor +{ + Magnum::Color4 m_color; + MeshIdOwner_t m_mesh; +}; + +Session setup_cursor( + Builder_t& rBuilder, + ArrayView const topData, + Tags& rTags, + Session const& magnum, + Session const& scnCommon, + Session const& scnRender, + Session const& cameraCtrl, + Session const& shFlat, + TopDataId const idResources, + PkgId const pkg) +{ + OSP_SESSION_UNPACK_TAGS(magnum, TESTAPP_APP_MAGNUM); + OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); + OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(cameraCtrl, TESTAPP_CAMERA_CTRL); + OSP_SESSION_UNPACK_TAGS(cameraCtrl, TESTAPP_CAMERA_CTRL); + OSP_SESSION_UNPACK_DATA(shFlat, TESTAPP_SHADER_FLAT); + + auto &rResources = top_get< Resources > (topData, idResources); + auto &rBasic = top_get< ACtxBasic > (topData, idBasic); + auto &rActiveIds = top_get< ActiveReg_t > (topData, idActiveIds); + auto &rDrawing = top_get< ACtxDrawing > (topData, idDrawing); + auto &rDrawingRes = top_get< ACtxDrawingRes > (topData, idDrawingRes); + + Session cursor; + OSP_SESSION_ACQUIRE_DATA(cursor, topData, TESTAPP_CURSOR); + cursor.m_tgCleanupEvt = tgCleanupMagnumEvt; + + auto &rCursorData = top_emplace(topData, idCursorData); + rCursorData.m_color = { 0.0f, 1.0f, 0.0f, 1.0f }; + rCursorData.m_mesh = SysRender::add_drawable_mesh(rDrawing, rDrawingRes, rResources, pkg, "cubewire"); + + cursor.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderMod, tgCamCtrlReq}).data( + "Render cursor", + TopDataIds_t{ idRenderGl, idCamera, idDrawingRes, idDrawShFlat, idCamCtrl, idCursorData }, + wrap_args([] (RenderGL& rRenderGl, Camera const& rCamera, ACtxDrawingRes const& rDrawingRes, ACtxDrawFlat& rDrawShFlat, ACtxCameraController const& rCamCtrl, Cursor& rCursorData) noexcept + { + ResId const cursorResId = rDrawingRes.m_meshToRes.at(rCursorData.m_mesh.value()); + MeshGlId const cursorMeshGlId = rRenderGl.m_resToMesh.at(cursorResId); + Mesh& rCursorMeshGl = rRenderGl.m_meshGl.get(cursorMeshGlId); + + ViewProjMatrix viewProj{rCamera.m_transform.inverted(), rCamera.perspective()}; + + auto const matrix = viewProj.m_viewProj * Matrix4::translation(rCamCtrl.m_target.value()); + + rDrawShFlat.m_shaderUntextured + .setColor(rCursorData.m_color) + .setTransformationProjectionMatrix(matrix) + .draw(rCursorMeshGl); + })); + + cursor.task() = rBuilder.task().assign({tgCleanupMagnumEvt}).data( + "Clean up cursor resource owners", + TopDataIds_t{ idDrawing, idCursorData}, + wrap_args([] (ACtxDrawing& rDrawing, Cursor& rCursorData) noexcept + { + rDrawing.m_meshRefCounts.ref_release(std::move(rCursorData.m_mesh)); + })); + + return cursor; +} + Session setup_uni_test_planets_renderer( Builder_t& rBuilder, @@ -460,7 +551,6 @@ Session setup_uni_test_planets_renderer( // Draw black hole Vector3 const blackHolePos = Vector3(mainToArea.transform_position({})) * scale; rDrawShVisual.m_shader - .setColor(0x0E0E0E_rgbf) .setTransformationMatrix( viewProj.m_view * Matrix4::translation(blackHolePos) @@ -469,7 +559,6 @@ Session setup_uni_test_planets_renderer( .draw(rSphereMeshGl); // Draw planets - rDrawShVisual.m_shader.setColor(0xFFFFFF_rgbf); for (std::size_t i = 0; i < rMainSpace.m_satCount; ++i) { Vector3g const relative = mainToArea.transform_position({x[i], y[i], z[i]}); @@ -478,7 +567,6 @@ Session setup_uni_test_planets_renderer( Quaterniond const rot{{qx[i], qy[i], qz[i]}, qw[i]}; rDrawShVisual.m_shader - .setColor(0xFFFFFF_rgbf) .setTransformationMatrix( viewProj.m_view * Matrix4::translation(relativeMeters) diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index 11b072de..6e214473 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -83,6 +83,17 @@ osp::Session setup_shader_phong( osp::Session const& scnRender, osp::Session const& material); +osp::Session setup_cursor( + Builder_t& rBuilder, + osp::ArrayView topData, + osp::Tags& rTags, + osp::Session const& magnum, + osp::Session const& scnCommon, + osp::Session const& scnRender, + osp::Session const& cameraCtrl, + osp::Session const& shFlat, + osp::TopDataId const idResources, + osp::PkgId const pkg); osp::Session setup_uni_test_planets_renderer( Builder_t& rBuilder, diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index b41103c6..518ada83 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -168,6 +168,7 @@ static MainView get_main_view() .m_rExec = g_exec, .m_rTaskData = g_taskData, .m_idResources = g_idResources, + .m_defaultPkg = g_defaultPkg }; } @@ -210,7 +211,7 @@ int main(int argc, char** argv) exit(-1); } - g_rendererSetup = it->second.m_setup(get_main_view(), g_defaultPkg, g_sceneSessions); + g_rendererSetup = it->second.m_setup(get_main_view(), g_sceneSessions); start_magnum_async(); } @@ -261,7 +262,7 @@ int debug_cli_loop() close_sessions(g_sceneSessions); // Close existing scene - g_rendererSetup = it->second.m_setup(get_main_view(), g_defaultPkg, g_sceneSessions); + g_rendererSetup = it->second.m_setup(get_main_view(), g_sceneSessions); start_magnum_async(); } } From 8c0d12fee814313699fb5aa5a63569e9caa17564 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Thu, 9 Mar 2023 17:20:25 -0800 Subject: [PATCH 04/35] Add cone default resource --- src/test_application/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index 518ada83..7ecdeb62 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -354,6 +355,7 @@ void load_a_bunch_of_stuff() { using namespace osp::restypes; using namespace Magnum; + using Primitives::ConeFlag; using Primitives::CylinderFlag; std::size_t const maxTags = 256; // aka: just two 64-bit integers @@ -412,11 +414,13 @@ void load_a_bunch_of_stuff() }; Trade::MeshData &&cylinder = Magnum::MeshTools::transform3D( Primitives::cylinderSolid(3, 16, 1.0f, CylinderFlag::CapEnds), Matrix4::rotationX(Deg(90)), 0); + Trade::MeshData &&cone = Magnum::MeshTools::transform3D( Primitives::coneSolid(3, 16, 1.0f, ConeFlag::CapEnd), Matrix4::rotationX(Deg(90)), 0); add_mesh_quick("cube", Primitives::cubeSolid()); add_mesh_quick("cubewire", Primitives::cubeWireframe()); add_mesh_quick("sphere", Primitives::icosphereSolid(2)); add_mesh_quick("cylinder", std::move(cylinder)); + add_mesh_quick("cone", std::move(cone)); add_mesh_quick("grid64solid", Primitives::grid3DSolid({63, 63})); OSP_LOG_INFO("Resource loading complete"); From 8a5484ec8bc101b8653f34a9c9988aa13b36a970 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Thu, 9 Mar 2023 17:26:35 -0800 Subject: [PATCH 05/35] Add "Thrust Indicator" --- src/osp/Active/parts.h | 2 +- .../activescenes/identifiers.h | 4 +- .../activescenes/scenarios.cpp | 6 +- .../activescenes/scene_renderer.cpp | 229 ++++++++++++++++-- .../activescenes/scene_renderer.h | 14 ++ 5 files changed, 229 insertions(+), 26 deletions(-) diff --git a/src/osp/Active/parts.h b/src/osp/Active/parts.h index ca4a0410..14b45393 100644 --- a/src/osp/Active/parts.h +++ b/src/osp/Active/parts.h @@ -50,7 +50,7 @@ struct Parts std::vector m_partToWeld; std::vector m_partTransformWeld; MapPartToMachines_t m_partToMachines; - std::vector m_partDirty; + std::vector m_partDirty; lgrn::IdRegistryStl m_weldIds; lgrn::IntArrayMultiMap m_weldToParts; diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index cab8c85d..89cb0b28 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -262,8 +262,8 @@ -#define OSP_DATA_TESTAPP_CURSOR 1, \ - idCursorData +#define OSP_DATA_TESTAPP_INDICATOR 1, \ + idIndicator diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 079cfa8d..cc9163e4 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -265,14 +265,16 @@ static ScenarioMap_t make_scenarios() newton, nwtGravSet, nwtGrav, shapeSpawnNwt, vehicleSpawnNwt, nwtRocketSet, rocketsNwt ] = unpack<24>(scene); - rendererOut.resize(6); - auto & [scnRender, cameraCtrl, shVisual, camThrow, vehicleCtrl, cameraVehicle] = unpack<6>(rendererOut); + rendererOut.resize(8); + auto & [scnRender, cameraCtrl, shVisual, shFlat, camThrow, vehicleCtrl, cameraVehicle, thrustIndicator] = unpack<8>(rendererOut); scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); shVisual = setup_shader_visualizer (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); + shFlat = setup_shader_flat (builder, rTopData, rTags, magnum, scnCommon, scnRender, {}); camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); vehicleCtrl = setup_vehicle_control (builder, rTopData, rTags, scnCommon, parts, signalsFloat, magnum); cameraVehicle = setup_camera_vehicle (builder, rTopData, rTags, magnum, scnCommon, parts, physics, cameraCtrl, vehicleCtrl); + thrustIndicator = setup_thrust_indicators (builder, rTopData, rTags, magnum, scnCommon, parts, signalsFloat, scnRender, cameraCtrl, shFlat, mainView.m_idResources, mainView.m_defaultPkg); setup_magnum_draw(mainView, magnum, scnCommon, scnRender); }; diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index cf7350d6..f4529cc2 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -32,6 +32,8 @@ #include #include + +#include #include #include #include @@ -41,6 +43,8 @@ #include #include +#include + #include // for the 0xrrggbb_rgbf and angle literals @@ -389,12 +393,161 @@ Session setup_shader_phong( return shPhong; } -struct Cursor +struct IndicatorMesh { Magnum::Color4 m_color; MeshIdOwner_t m_mesh; }; + +Session setup_thrust_indicators( + Builder_t& rBuilder, + ArrayView const topData, + Tags& rTags, + Session const& magnum, + Session const& scnCommon, + Session const& parts, + Session const& signalsFloat, + Session const& scnRender, + Session const& cameraCtrl, + Session const& shFlat, + TopDataId const idResources, + PkgId const pkg) +{ + using namespace osp::link; + using adera::gc_mtMagicRocket; + using adera::ports_magicrocket::gc_throttleIn; + using adera::ports_magicrocket::gc_multiplierIn; + + static constexpr float indicatorScale = 0.0001f; + + OSP_SESSION_UNPACK_TAGS(magnum, TESTAPP_APP_MAGNUM); + OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); + OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); + OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); + OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) + OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); + OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); + + OSP_SESSION_UNPACK_DATA(cameraCtrl, TESTAPP_CAMERA_CTRL); + OSP_SESSION_UNPACK_TAGS(cameraCtrl, TESTAPP_CAMERA_CTRL); + OSP_SESSION_UNPACK_DATA(shFlat, TESTAPP_SHADER_FLAT); + + auto &rResources = top_get< Resources > (topData, idResources); + auto &rBasic = top_get< ACtxBasic > (topData, idBasic); + auto &rActiveIds = top_get< ActiveReg_t > (topData, idActiveIds); + auto &rDrawing = top_get< ACtxDrawing > (topData, idDrawing); + auto &rDrawingRes = top_get< ACtxDrawingRes > (topData, idDrawingRes); + + Session thrustIndicator; + OSP_SESSION_ACQUIRE_DATA(thrustIndicator, topData, TESTAPP_INDICATOR); + thrustIndicator.m_tgCleanupEvt = tgCleanupMagnumEvt; + + auto &rIndicator = top_emplace(topData, idIndicator); + rIndicator.m_color = { 1.0f, 0.0f, 0.0f, 1.0f }; + rIndicator.m_mesh = SysRender::add_drawable_mesh(rDrawing, rDrawingRes, rResources, pkg, "cone"); + + thrustIndicator.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgDrawTransformReq, tgBindFboReq, tgFwdRenderMod, tgCamCtrlReq}).data( + "Render cursor", + TopDataIds_t{ idRenderGl, idCamera, idDrawingRes, idScnRender, idScnParts, idSigValFloat, idDrawShFlat, idCamCtrl, idIndicator}, + wrap_args([] (RenderGL& rRenderGl, Camera const& rCamera, ACtxDrawingRes const& rDrawingRes, ACtxSceneRenderGL const& rScnRender, ACtxParts const& rScnParts, SignalValues_t const& rSigValFloat, ACtxDrawFlat& rDrawShFlat, ACtxCameraController const& rCamCtrl, IndicatorMesh& rIndicator) noexcept + { + + ResId const indicatorResId = rDrawingRes.m_meshToRes.at(rIndicator.m_mesh.value()); + MeshGlId const indicatorMeshGlId = rRenderGl.m_resToMesh.at(indicatorResId); + Mesh& rIndicatorMeshGl = rRenderGl.m_meshGl.get(indicatorMeshGlId); + + ViewProjMatrix viewProj{rCamera.m_transform.inverted(), rCamera.perspective()}; + + //auto const matrix = viewProj.m_viewProj * Matrix4::translation(rCamCtrl.m_target.value()); + + PerMachType const& rockets = rScnParts.m_machines.m_perType[gc_mtMagicRocket]; + Nodes const& floats = rScnParts.m_nodePerType[gc_ntSigFloat]; + + for (MachLocalId const localId : rockets.m_localIds.bitview().zeros()) + { + MachAnyId const anyId = rockets.m_localToAny[localId]; + PartId const part = rScnParts.m_machineToPart[anyId]; + ActiveEnt const partEnt = rScnParts.m_partToActive[part]; + + auto const& portSpan = floats.m_machToNode[anyId]; + NodeId const throttleIn = connected_node(portSpan, gc_throttleIn.m_port); + NodeId const multiplierIn = connected_node(portSpan, gc_multiplierIn.m_port); + + float const throttle = std::clamp(rSigValFloat[throttleIn], 0.0f, 1.0f); + float const multiplier = rSigValFloat[multiplierIn]; + float const thrustMag = throttle * multiplier; + + if (thrustMag == 0.0f) + { + continue; + } + + Matrix4 const& rocketDrawTf = rScnRender.m_drawTransform.get(partEnt); + + auto const& matrix = Matrix4::from(rocketDrawTf.rotationNormalized(), rocketDrawTf.translation()) + * Matrix4::scaling({1.0f, 1.0f, thrustMag * indicatorScale}) + * Matrix4::translation({0.0f, 0.0f, -1.0f}) + * Matrix4::scaling({0.2f, 0.2f, 1.0f}); + + rDrawShFlat.m_shaderUntextured + .setColor(rIndicator.m_color) + .setTransformationProjectionMatrix(viewProj.m_viewProj * matrix) + .draw(rIndicatorMeshGl); + } + })); + + thrustIndicator.task() = rBuilder.task().assign({tgCleanupMagnumEvt}).data( + "Clean up thrust indicator resource owners", + TopDataIds_t{ idDrawing, idIndicator}, + wrap_args([] (ACtxDrawing& rDrawing, IndicatorMesh& rIndicator) noexcept + { + rDrawing.m_meshRefCounts.ref_release(std::move(rIndicator.m_mesh)); + })); + + // Draw transforms in part entities are required for drawing indicators. + // This solution of adding them here is a bit janky but it works. + + thrustIndicator.task() = rBuilder.task().assign({tgSyncEvt, tgPartReq}).data( + "Add draw transforms to rocket entities", + TopDataIds_t{ idScnParts, idScnRender}, + wrap_args([] (ACtxParts const& rScnParts, ACtxSceneRenderGL& rScnRender) noexcept + { + for (PartId const part : rScnParts.m_partDirty) + { + // TODO: maybe first check if the part contains any rocket machines + + ActiveEnt const ent = rScnParts.m_partToActive[part]; + if ( ! rScnRender.m_drawTransform.contains(ent) ) + { + rScnRender.m_drawTransform.emplace(ent); + } + } + })); + + // Called when scene is reopened + thrustIndicator.task() = rBuilder.task().assign({tgResyncEvt, tgPartReq}).data( + "Add draw transforms to all rocket entities", + TopDataIds_t{ idScnParts, idScnRender}, + wrap_args([] (ACtxParts const& rScnParts, ACtxSceneRenderGL& rScnRender) noexcept + { + for (PartId const part : rScnParts.m_partIds.bitview().zeros()) + { + ActiveEnt const ent = rScnParts.m_partToActive[part]; + if ( ! rScnRender.m_drawTransform.contains(ent) ) + { + rScnRender.m_drawTransform.emplace(ent); + } + } + })); + + return thrustIndicator; +} + + Session setup_cursor( Builder_t& rBuilder, ArrayView const topData, @@ -424,19 +577,19 @@ Session setup_cursor( auto &rDrawingRes = top_get< ACtxDrawingRes > (topData, idDrawingRes); Session cursor; - OSP_SESSION_ACQUIRE_DATA(cursor, topData, TESTAPP_CURSOR); + OSP_SESSION_ACQUIRE_DATA(cursor, topData, TESTAPP_INDICATOR); cursor.m_tgCleanupEvt = tgCleanupMagnumEvt; - auto &rCursorData = top_emplace(topData, idCursorData); + auto &rCursorData = top_emplace(topData, idIndicator); rCursorData.m_color = { 0.0f, 1.0f, 0.0f, 1.0f }; rCursorData.m_mesh = SysRender::add_drawable_mesh(rDrawing, rDrawingRes, rResources, pkg, "cubewire"); cursor.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderMod, tgCamCtrlReq}).data( "Render cursor", - TopDataIds_t{ idRenderGl, idCamera, idDrawingRes, idDrawShFlat, idCamCtrl, idCursorData }, - wrap_args([] (RenderGL& rRenderGl, Camera const& rCamera, ACtxDrawingRes const& rDrawingRes, ACtxDrawFlat& rDrawShFlat, ACtxCameraController const& rCamCtrl, Cursor& rCursorData) noexcept + TopDataIds_t{ idRenderGl, idCamera, idDrawingRes, idDrawShFlat, idCamCtrl, idIndicator }, + wrap_args([] (RenderGL& rRenderGl, Camera const& rCamera, ACtxDrawingRes const& rDrawingRes, ACtxDrawFlat& rDrawShFlat, ACtxCameraController const& rCamCtrl, IndicatorMesh& rIndicator) noexcept { - ResId const cursorResId = rDrawingRes.m_meshToRes.at(rCursorData.m_mesh.value()); + ResId const cursorResId = rDrawingRes.m_meshToRes.at(rIndicator.m_mesh.value()); MeshGlId const cursorMeshGlId = rRenderGl.m_resToMesh.at(cursorResId); Mesh& rCursorMeshGl = rRenderGl.m_meshGl.get(cursorMeshGlId); @@ -445,17 +598,17 @@ Session setup_cursor( auto const matrix = viewProj.m_viewProj * Matrix4::translation(rCamCtrl.m_target.value()); rDrawShFlat.m_shaderUntextured - .setColor(rCursorData.m_color) + .setColor(rIndicator.m_color) .setTransformationProjectionMatrix(matrix) .draw(rCursorMeshGl); })); cursor.task() = rBuilder.task().assign({tgCleanupMagnumEvt}).data( "Clean up cursor resource owners", - TopDataIds_t{ idDrawing, idCursorData}, - wrap_args([] (ACtxDrawing& rDrawing, Cursor& rCursorData) noexcept + TopDataIds_t{ idDrawing, idIndicator}, + wrap_args([] (ACtxDrawing& rDrawing, IndicatorMesh& rIndicator) noexcept { - rDrawing.m_meshRefCounts.ref_release(std::move(rCursorData.m_mesh)); + rDrawing.m_meshRefCounts.ref_release(std::move(rIndicator.m_mesh)); })); return cursor; @@ -494,15 +647,37 @@ Session setup_uni_test_planets_renderer( //OSP_SESSION_ACQUIRE_DATA(uniTestPlanets, topData, TESTAPP_UNI_PLANETS); - uniTestPlanetsRdr.task() = rBuilder.task().assign({tgRenderEvt, tgCamCtrlReq}).data( + uniTestPlanetsRdr.task() = rBuilder.task().assign({tgRenderEvt, tgScnFramePosMod, tgCamCtrlMod}).data( "Position SceneFrame center Camera Controller", - TopDataIds_t{ idCamCtrl, idScnFrame}, - wrap_args([] (ACtxCameraController const& rCamCtrl, SceneFrame& rScnFrame) noexcept + TopDataIds_t{ idCamCtrl, idScnFrame}, + wrap_args([] (ACtxCameraController& rCamCtrl, SceneFrame& rScnFrame) noexcept { - rScnFrame.m_scenePosition = Vector3g(math::mul_2pow( rCamCtrl.m_target.value(), rScnFrame.m_precision)); + if ( ! rCamCtrl.m_target.has_value()) + { + return; + } + Vector3 &rCamTgt = rCamCtrl.m_target.value(); + + // check origin translation + // ADL used for Magnum::Math::sign/floor/abs + float const maxDist = 512.0f; + Vector3 const translate = sign(rCamTgt) * floor(abs(rCamTgt) / maxDist) * maxDist; + + if ( ! translate.isZero()) + { + rCamCtrl.m_transform.translation() -= translate; + rCamTgt -= translate; + + // a bit janky to modify universe stuff directly here, but it works lol + Vector3 const rotated = Quaternion(rScnFrame.m_rotation).transformVector(translate); + rScnFrame.m_position += Vector3g(math::mul_2pow(rotated, rScnFrame.m_precision)); + } + + rScnFrame.m_scenePosition = Vector3g(math::mul_2pow(rCamCtrl.m_target.value(), rScnFrame.m_precision)); + })); - uniTestPlanetsRdr.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderMod, tgDrawReq, tgCameraReq}).data( + uniTestPlanetsRdr.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderMod, tgDrawReq, tgCameraReq, tgScnFramePosReq}).data( "Render test planets", TopDataIds_t{ idRenderGl, idCamera, idDrawShVisual, idDrawingRes, idNMesh, idUniverse, idScnFrame, idPlanetMainSpace}, wrap_args([] (RenderGL& rRenderGl, Camera const& rCamera, ACtxDrawMeshVisualizer& rDrawShVisual, ACtxDrawingRes const& rDrawingRes, NamedMeshes& rNMesh, Universe& rUniverse, SceneFrame const& rScnFrame, CoSpaceId const planetMainSpace) noexcept @@ -546,16 +721,28 @@ Session setup_uni_test_planets_renderer( Renderer::enable(Renderer::Feature::DepthTest); Renderer::enable(Renderer::Feature::FaceCulling); Renderer::disable(Renderer::Feature::Blending); - //Renderer::setDepthMask(true); + Renderer::setDepthMask(true); - // Draw black hole - Vector3 const blackHolePos = Vector3(mainToArea.transform_position({})) * scale; + // Draw center indicator + Vector3 const centerPos = Vector3(mainToArea.transform_position({})) * scale; rDrawShVisual.m_shader .setTransformationMatrix( viewProj.m_view - * Matrix4::translation(blackHolePos) - * Matrix4::scaling({200, 200, 200}) - * Matrix4{mainToAreaRot.toMatrix()} ) + * Matrix4::translation(centerPos) + * Matrix4{mainToAreaRot.toMatrix()} + * Matrix4::scaling({500, 50, 50})) + .draw(rSphereMeshGl) + .setTransformationMatrix( + viewProj.m_view + * Matrix4::translation(centerPos) + * Matrix4{mainToAreaRot.toMatrix()} + * Matrix4::scaling({50, 500, 50})) + .draw(rSphereMeshGl) + .setTransformationMatrix( + viewProj.m_view + * Matrix4::translation(centerPos) + * Matrix4{mainToAreaRot.toMatrix()} + * Matrix4::scaling({50, 50, 500}) ) .draw(rSphereMeshGl); // Draw planets diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index 6e214473..7ce9f4a4 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -83,6 +83,20 @@ osp::Session setup_shader_phong( osp::Session const& scnRender, osp::Session const& material); +osp::Session setup_thrust_indicators( + Builder_t& rBuilder, + osp::ArrayView topData, + osp::Tags& rTags, + osp::Session const& magnum, + osp::Session const& scnCommon, + osp::Session const& parts, + osp::Session const& signalsFloat, + osp::Session const& scnRender, + osp::Session const& cameraCtrl, + osp::Session const& shFlat, + osp::TopDataId const idResources, + osp::PkgId const pkg); + osp::Session setup_cursor( Builder_t& rBuilder, osp::ArrayView topData, From 247f014c702daf130107bd62a8943489716af049 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sat, 11 Mar 2023 14:16:17 -0800 Subject: [PATCH 06/35] Clean up various stuff --- src/osp/Shaders/Phong.cpp | 34 ++++++++++++++++--- src/osp/unpack.h | 7 ++++ .../activescenes/scenarios.cpp | 18 ++++------ .../activescenes/scene_renderer.h | 19 +++++++++-- .../activescenes/scene_universe.cpp | 16 ++++----- .../activescenes/scene_universe.h | 10 +++++- 6 files changed, 76 insertions(+), 28 deletions(-) diff --git a/src/osp/Shaders/Phong.cpp b/src/osp/Shaders/Phong.cpp index 208d2d88..a410ab60 100644 --- a/src/osp/Shaders/Phong.cpp +++ b/src/osp/Shaders/Phong.cpp @@ -78,15 +78,39 @@ void shader::draw_ent_phong( MeshGlId const meshId = rData.m_pMeshId->get(ent).m_glId; Magnum::GL::Mesh &rMesh = rData.m_pMeshGl->get(meshId); + Matrix3 a{viewProj.m_view}; + + + // Lights with w=0.0f are directional lights + // Directonal lights are camera-relative, so we need 'viewProj.m_view *' + auto const lightPositions = + { + viewProj.m_view * Vector4{ Vector3{0.2f, 0.6f, 0.5f}.normalized(), 0.0f}, + viewProj.m_view * Vector4{-Vector3{0.0f, 0.0f, 1.0f}, 0.0f} + }; + + auto const lightColors = + { + 0xddd4Cd_rgbf, + 0x32354e_rgbf + }; + + auto const lightSpecColors = + { + 0xfff5ed_rgbf, + 0x000000_rgbf + }; + + // TODO: find a better way to deal with lights instead of hard-coding it rShader - .setAmbientColor(0x000000ff_rgbaf) + .setAmbientColor(0x1a1e29ff_rgbaf) .setSpecularColor(0xffffff00_rgbaf) - .setLightColors({0xfff5ec_rgbf, 0xe4e8ff_rgbf}) - .setLightPositions({ Vector4{ Vector3{0.2f, 0.6f, 0.5f}.normalized(), 0.0f}, - Vector4{-Vector3{0.2f, 0.6f, 0.5f}.normalized(), 0.0f} }) + .setLightColors(lightColors) + .setLightSpecularColors(lightSpecColors) + .setLightPositions(lightPositions) .setTransformationMatrix(entRelative) .setProjectionMatrix(viewProj.m_proj) - .setNormalMatrix(Matrix3{drawTf}) + .setNormalMatrix(entRelative.normalMatrix()) .draw(rMesh); } diff --git a/src/osp/unpack.h b/src/osp/unpack.h index f10e9e2d..26c7fb3a 100644 --- a/src/osp/unpack.h +++ b/src/osp/unpack.h @@ -45,6 +45,13 @@ constexpr auto& unpack(RANGE_T &rIn) return *reinterpret_cast(std::data(rIn)); } +template +constexpr auto& resize_then_unpack(CONTAINER_T &rIn) +{ + rIn.resize(N); + return unpack(rIn); +} + } diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index cc9163e4..8bbfa864 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -143,8 +143,7 @@ static ScenarioMap_t make_scenarios() auto &rTags = mainView.m_rTags; Builder_t builder{rTags, mainView.m_rTasks, mainView.m_rTaskData}; - sceneOut.resize(10); - auto & [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt] = unpack<10>(sceneOut); + auto & [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt] = resize_then_unpack<10>(sceneOut); // Compose together lots of Sessions scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); @@ -169,8 +168,7 @@ static ScenarioMap_t make_scenarios() auto const& [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt] = unpack<10>(scene); - rendererOut.resize(5); - auto & [scnRender, cameraCtrl, cameraFree, shVisual, camThrow] = unpack<5>(rendererOut); + auto & [scnRender, cameraCtrl, cameraFree, shVisual, camThrow] = resize_then_unpack<5>(rendererOut); scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); cameraFree = setup_camera_free (builder, rTopData, rTags, magnum, scnCommon, cameraCtrl); @@ -192,7 +190,6 @@ static ScenarioMap_t make_scenarios() auto &rTags = mainView.m_rTags; Builder_t builder{rTags, mainView.m_rTasks, mainView.m_rTaskData}; - sceneOut.resize(24); auto & [ scnCommon, matVisual, physics, shapeSpawn, @@ -201,7 +198,7 @@ static ScenarioMap_t make_scenarios() signalsFloat, machRocket, machRcsDriver, testVehicles, droppers, gravity, bounds, thrower, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, vehicleSpawnNwt, nwtRocketSet, rocketsNwt - ] = unpack<24>(sceneOut); + ] = resize_then_unpack<24>(sceneOut); scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); matVisual = setup_material (builder, rTopData, rTags, scnCommon); @@ -265,11 +262,10 @@ static ScenarioMap_t make_scenarios() newton, nwtGravSet, nwtGrav, shapeSpawnNwt, vehicleSpawnNwt, nwtRocketSet, rocketsNwt ] = unpack<24>(scene); - rendererOut.resize(8); - auto & [scnRender, cameraCtrl, shVisual, shFlat, camThrow, vehicleCtrl, cameraVehicle, thrustIndicator] = unpack<8>(rendererOut); + auto & [scnRender, cameraCtrl, shPhong, shFlat, camThrow, vehicleCtrl, cameraVehicle, thrustIndicator] = resize_then_unpack<8>(rendererOut); scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); - shVisual = setup_shader_visualizer (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); + shPhong = setup_shader_phong (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); shFlat = setup_shader_flat (builder, rTopData, rTags, magnum, scnCommon, scnRender, {}); camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); vehicleCtrl = setup_vehicle_control (builder, rTopData, rTags, scnCommon, parts, signalsFloat, magnum); @@ -290,10 +286,10 @@ static ScenarioMap_t make_scenarios() auto &rTags = mainView.m_rTags; Builder_t builder{rTags, mainView.m_rTasks, mainView.m_rTaskData}; - sceneOut.resize(13); auto & [ - scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(sceneOut); + scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets + ] = resize_then_unpack<13>(sceneOut); // Compose together lots of Sessions scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index 7ce9f4a4..47151b0d 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -52,7 +52,7 @@ osp::Session setup_scene_renderer( osp::TopDataId idResources); /** - * @brief Assign a material from setup_material to use Magnum MeshVisualizer + * @brief Magnum MeshVisualizer shader and optional material for drawing ActiveEnts with it */ osp::Session setup_shader_visualizer( Builder_t& rBuilder, @@ -63,7 +63,9 @@ osp::Session setup_shader_visualizer( osp::Session const& scnRender, osp::Session const& material); - +/** + * @brief Magnum Flat shader and optional material for drawing ActiveEnts with it + */ osp::Session setup_shader_flat( Builder_t& rBuilder, osp::ArrayView topData, @@ -73,7 +75,9 @@ osp::Session setup_shader_flat( osp::Session const& scnRender, osp::Session const& material); - +/** + * @brief Magnum Phong shader and optional material for drawing ActiveEnts with it + */ osp::Session setup_shader_phong( Builder_t& rBuilder, osp::ArrayView topData, @@ -83,6 +87,9 @@ osp::Session setup_shader_phong( osp::Session const& scnRender, osp::Session const& material); +/** + * @brief Red indicators over Magic Rockets + */ osp::Session setup_thrust_indicators( Builder_t& rBuilder, osp::ArrayView topData, @@ -97,6 +104,9 @@ osp::Session setup_thrust_indicators( osp::TopDataId const idResources, osp::PkgId const pkg); +/** + * @brief Wireframe cube over the camera controller's target + */ osp::Session setup_cursor( Builder_t& rBuilder, osp::ArrayView topData, @@ -109,6 +119,9 @@ osp::Session setup_cursor( osp::TopDataId const idResources, osp::PkgId const pkg); +/** + * @brief Draw universe, specifically designed for setup_uni_test_planets + */ osp::Session setup_uni_test_planets_renderer( Builder_t& rBuilder, osp::ArrayView topData, diff --git a/src/test_application/activescenes/scene_universe.cpp b/src/test_application/activescenes/scene_universe.cpp index ee4c3ebd..9f4d2a53 100644 --- a/src/test_application/activescenes/scene_universe.cpp +++ b/src/test_application/activescenes/scene_universe.cpp @@ -41,7 +41,6 @@ using namespace osp::universe; namespace testapp::scenes { - Session setup_uni_core( Builder_t& rBuilder, ArrayView topData, @@ -56,20 +55,21 @@ Session setup_uni_core( return uniCore; } + Session setup_uni_sceneframe( Builder_t& rBuilder, ArrayView topData, Tags& rTags) { - Session ScnFrame; - OSP_SESSION_ACQUIRE_DATA(ScnFrame, topData, TESTAPP_UNI_SCENEFRAME); - OSP_SESSION_ACQUIRE_TAGS(ScnFrame, rTags, TESTAPP_UNI_SCENEFRAME); + Session scnFrame; + OSP_SESSION_ACQUIRE_DATA(scnFrame, topData, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_ACQUIRE_TAGS(scnFrame, rTags, TESTAPP_UNI_SCENEFRAME); rBuilder.tag(tgScnFramePosReq).depend_on({tgScnFramePosMod}); top_emplace< SceneFrame > (topData, idScnFrame); - return ScnFrame; + return scnFrame; } @@ -78,15 +78,15 @@ Session setup_uni_test_planets( ArrayView topData, Tags& rTags, Session const& uniCore, - Session const& ScnFrame) + Session const& scnFrame) { using CoSpaceIdVec_t = std::vector; using Corrade::Containers::Array; OSP_SESSION_UNPACK_TAGS(uniCore, TESTAPP_UNI_CORE); OSP_SESSION_UNPACK_DATA(uniCore, TESTAPP_UNI_CORE); - OSP_SESSION_UNPACK_TAGS(ScnFrame, TESTAPP_UNI_SCENEFRAME); - OSP_SESSION_UNPACK_DATA(ScnFrame, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_UNPACK_TAGS(scnFrame, TESTAPP_UNI_SCENEFRAME); + OSP_SESSION_UNPACK_DATA(scnFrame, TESTAPP_UNI_SCENEFRAME); auto &rUniverse = top_get< Universe >(topData, idUniverse); diff --git a/src/test_application/activescenes/scene_universe.h b/src/test_application/activescenes/scene_universe.h index 25fc7935..9cafb953 100644 --- a/src/test_application/activescenes/scene_universe.h +++ b/src/test_application/activescenes/scene_universe.h @@ -29,17 +29,25 @@ namespace testapp::scenes { +/** + * @brief Core Universe struct with addressable Coordinate Spaces + */ osp::Session setup_uni_core( Builder_t& rBuilder, osp::ArrayView topData, osp::Tags& rTags); - +/** + * @brief Represents the physics scene's presence in a Universe + */ osp::Session setup_uni_sceneframe( Builder_t& rBuilder, osp::ArrayView topData, osp::Tags& rTags); +/** + * @brief Unrealistic planets test, allows SceneFrame to move around and get captured into planets + */ osp::Session setup_uni_test_planets( Builder_t& rBuilder, osp::ArrayView topData, From 0d1adbb8332b179aeb110346dd82429eac9b7903 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 10 Apr 2023 17:32:38 -0700 Subject: [PATCH 07/35] Fix machines initialization order fiasco --- src/adera/machines/links.cpp | 4 ---- src/adera/machines/links.h | 6 +++--- src/osp/link/machines.cpp | 3 --- src/osp/link/machines.h | 2 +- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/adera/machines/links.cpp b/src/adera/machines/links.cpp index 0747ed2d..eb5fd6e2 100644 --- a/src/adera/machines/links.cpp +++ b/src/adera/machines/links.cpp @@ -32,10 +32,6 @@ using osp::link::MachTypeId; namespace adera { -MachTypeId const gc_mtUserCtrl = MachTypeReg_t::create(); -MachTypeId const gc_mtMagicRocket = MachTypeReg_t::create(); -MachTypeId const gc_mtRcsDriver = MachTypeReg_t::create(); - float thruster_influence(Vector3 const pos, Vector3 const dir, Vector3 const cmdLin, Vector3 const cmdAng) noexcept { using Magnum::Math::cross; diff --git a/src/adera/machines/links.h b/src/adera/machines/links.h index 68abe65b..aa70da3b 100644 --- a/src/adera/machines/links.h +++ b/src/adera/machines/links.h @@ -35,9 +35,9 @@ using osp::link::gc_ntSigFloat; using osp::link::gc_sigIn; using osp::link::gc_sigOut; -extern osp::link::MachTypeId const gc_mtUserCtrl; -extern osp::link::MachTypeId const gc_mtMagicRocket; -extern osp::link::MachTypeId const gc_mtRcsDriver; +inline osp::link::MachTypeId const gc_mtUserCtrl = osp::link::MachTypeReg_t::create(); +inline osp::link::MachTypeId const gc_mtMagicRocket = osp::link::MachTypeReg_t::create(); +inline osp::link::MachTypeId const gc_mtRcsDriver = osp::link::MachTypeReg_t::create(); constexpr osp::Vector3 gc_rocketForward{0.0f, 0.0f, 1.0f}; diff --git a/src/osp/link/machines.cpp b/src/osp/link/machines.cpp index b25592e8..5524b20c 100644 --- a/src/osp/link/machines.cpp +++ b/src/osp/link/machines.cpp @@ -27,9 +27,6 @@ namespace osp::link { -NodeTypeId const gc_ntSigFloat = NodeTypeReg_t::create(); - - void copy_nodes( Nodes const &rSrcNodes, Machines const &rSrcMach, diff --git a/src/osp/link/machines.h b/src/osp/link/machines.h index 4795c83a..629208a8 100644 --- a/src/osp/link/machines.h +++ b/src/osp/link/machines.h @@ -51,7 +51,7 @@ using JuncCustom = uint16_t; using MachTypeReg_t = GlobalIdReg; using NodeTypeReg_t = GlobalIdReg; -extern NodeTypeId const gc_ntSigFloat; +inline NodeTypeId const gc_ntSigFloat = NodeTypeReg_t::create(); /** * @brief Keeps track of Machines of a certain type that exists From 3b26242ca263821801ee1e69fb8e966784402abb Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Wed, 12 Apr 2023 22:32:54 -0700 Subject: [PATCH 08/35] Screw with renderer again --- src/adera/Shaders/PlumeShader.cpp | 2 +- src/osp/Active/SysPrefabInit.cpp | 23 +++++----- src/osp/Active/SysPrefabInit.h | 2 +- src/osp/Active/SysRender.cpp | 38 ++++++++++------- src/osp/Active/SysRender.h | 42 +++++++++---------- src/osp/Active/activetypes.h | 9 ---- src/osp/Active/drawing.h | 54 ++++++++++++------------ src/osp/Active/opengl/SysRenderGL.cpp | 60 +++++++++++++-------------- src/osp/Active/opengl/SysRenderGL.h | 40 +++++++++--------- src/osp/Shaders/Flat.cpp | 12 +++--- src/osp/Shaders/Flat.h | 20 ++++----- src/osp/Shaders/MeshVisualizer.cpp | 6 +-- src/osp/Shaders/MeshVisualizer.h | 8 ++-- src/osp/Shaders/Phong.cpp | 12 +++--- src/osp/Shaders/Phong.h | 22 +++++----- src/osp/keyed_bitview.h | 0 src/osp/vecmap.h | 0 17 files changed, 172 insertions(+), 178 deletions(-) create mode 100644 src/osp/keyed_bitview.h create mode 100644 src/osp/vecmap.h diff --git a/src/adera/Shaders/PlumeShader.cpp b/src/adera/Shaders/PlumeShader.cpp index 7fde5dd2..7b885820 100644 --- a/src/adera/Shaders/PlumeShader.cpp +++ b/src/adera/Shaders/PlumeShader.cpp @@ -106,7 +106,7 @@ void PlumeShader::assign_plumes( { for (ActiveEnt ent : entities) { - rStorage.emplace(ent, EntityToDraw{&draw_plume, {&rData} }); + //rStorage.emplace(ent, EntityToDraw{&draw_plume, {&rData} }); } } diff --git a/src/osp/Active/SysPrefabInit.cpp b/src/osp/Active/SysPrefabInit.cpp index ece0f0f7..91f652fc 100644 --- a/src/osp/Active/SysPrefabInit.cpp +++ b/src/osp/Active/SysPrefabInit.cpp @@ -116,7 +116,7 @@ void SysPrefabInit::init_drawing( Resources& rResources, ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, - std::optional material) noexcept + std::optional material) noexcept { auto itPfEnts = std::begin(rPrefabInit.m_ents); @@ -137,7 +137,7 @@ void SysPrefabInit::init_drawing( // which considers them for draw transformations. // Only set drawable for entities that have a mesh or is an // ancestor of an entity with a mesh. - rDrawing.m_drawable.set(std::size_t(ent)); + //rDrawing.m_drawable.set(std::size_t(ent)); // Check if object has mesh int const meshImportId = rImportData.m_objMeshes[objects[i]]; @@ -146,10 +146,13 @@ void SysPrefabInit::init_drawing( continue; } + DrawEnt const drawEnt = rDrawing.m_drawIds.create(); + rDrawing.m_activeToDraw[ent] = drawEnt; + osp::ResId const meshRes = rImportData.m_meshes[meshImportId]; MeshId const meshId = SysRender::own_mesh_resource(rDrawing, rDrawingRes, rResources, meshRes); - rDrawing.m_mesh.emplace(ent, rDrawing.m_meshRefCounts.ref_add(meshId)); - rDrawing.m_meshDirty.push_back(ent); + rDrawing.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(meshId); + rDrawing.m_meshDirty.push_back(drawEnt); int const matImportId = rImportData.m_objMaterials[objects[i]]; @@ -162,19 +165,19 @@ void SysPrefabInit::init_drawing( { osp::ResId const texRes = rImportData.m_textures[baseColor]; TexId const texId = SysRender::own_texture_resource(rDrawing, rDrawingRes, rResources, texRes); - rDrawing.m_diffuseTex.emplace(ent, rDrawing.m_texRefCounts.ref_add(texId)); - rDrawing.m_diffuseDirty.push_back(ent); + rDrawing.m_diffuseTex[drawEnt] = rDrawing.m_texRefCounts.ref_add(texId); + rDrawing.m_diffuseDirty.push_back(drawEnt); } } - rDrawing.m_opaque.emplace(ent); - rDrawing.m_visible.emplace(ent); + rDrawing.m_drawBasic[drawEnt] = { .m_opaque = true, .m_transparent = false }; + rDrawing.m_visible.set(std::size_t(drawEnt)); if (material.has_value()) { - material.value().m_rEnts.set(std::size_t(ent)); - material.value().m_rDirty.push_back(ent); + material.value().m_rEnts.set(std::size_t(drawEnt)); + material.value().m_rDirty.push_back(drawEnt); } } diff --git a/src/osp/Active/SysPrefabInit.h b/src/osp/Active/SysPrefabInit.h index 3dda256b..bbe1a19e 100644 --- a/src/osp/Active/SysPrefabInit.h +++ b/src/osp/Active/SysPrefabInit.h @@ -92,7 +92,7 @@ class SysPrefabInit Resources& rResources, ACtxDrawing& rDrawing, ACtxDrawingRes& rCtxDrawRes, - std::optional material) noexcept; + std::optional material) noexcept; static void init_physics( ACtxPrefabInit const& rPrefabInit, diff --git a/src/osp/Active/SysRender.cpp b/src/osp/Active/SysRender.cpp index dd0f6a28..7f870fc7 100644 --- a/src/osp/Active/SysRender.cpp +++ b/src/osp/Active/SysRender.cpp @@ -91,13 +91,23 @@ void SysRender::set_dirty_all(ACtxDrawing &rCtxDrawing) { using osp::active::active_sparse_set_t; - // Set all meshs dirty - auto &rMeshSet = static_cast(rCtxDrawing.m_mesh); - rCtxDrawing.m_meshDirty.assign(std::begin(rMeshSet), std::end(rMeshSet)); - // Set all textures dirty - auto &rDiffSet = static_cast(rCtxDrawing.m_diffuseTex); - rCtxDrawing.m_diffuseDirty.assign(std::begin(rMeshSet), std::end(rMeshSet)); + for (std::size_t const drawEntInt : rCtxDrawing.m_drawIds.bitview().zeros()) + { + auto const drawEnt = DrawEnt(drawEntInt); + + // Set all meshs dirty + if (rCtxDrawing.m_mesh[drawEnt] != lgrn::id_null()) + { + rCtxDrawing.m_meshDirty.push_back(drawEnt); + } + + // Set all textures dirty + if (rCtxDrawing.m_diffuseTex[drawEnt] != lgrn::id_null()) + { + rCtxDrawing.m_diffuseDirty.push_back(drawEnt); + } + } } void SysRender::clear_dirty_all(ACtxDrawing& rCtxDrawing) @@ -109,9 +119,10 @@ void SysRender::clear_dirty_all(ACtxDrawing& rCtxDrawing) void SysRender::update_draw_transforms_recurse( ACtxSceneGraph const& rScnGraph, + KeyedVec const& activeToDraw, acomp_storage_t const& rTf, - acomp_storage_t& rDrawTf, - EntSet_t const& rDrawable, + DrawTransforms_t& rDrawTf, + EntSet_t const& needDrawTf, ActiveEnt ent, Matrix4 const& parentTf, bool root) @@ -119,20 +130,19 @@ void SysRender::update_draw_transforms_recurse( Matrix4 const& entTf = rTf.get(ent).m_transform; Matrix4 const& entDrawTf = root ? (entTf) : (parentTf * entTf); - if (rDrawTf.contains(ent)) + if (DrawEnt const drawEnt = activeToDraw[ent]; + drawEnt != lgrn::id_null()) { - rDrawTf.get(ent) = entDrawTf; + rDrawTf[drawEnt] = entDrawTf; } for (ActiveEnt entChild : SysSceneGraph::children(rScnGraph, ent)) { - if (rDrawable.test(std::size_t(entChild))) + if (needDrawTf.test(std::size_t(entChild))) { - update_draw_transforms_recurse(rScnGraph, rTf, rDrawTf, rDrawable, entChild, entDrawTf, false); + update_draw_transforms_recurse(rScnGraph, activeToDraw, rTf, rDrawTf, needDrawTf, entChild, entDrawTf, false); } } - - //parentTf. } diff --git a/src/osp/Active/SysRender.h b/src/osp/Active/SysRender.h index 86b22ef2..7bf24384 100644 --- a/src/osp/Active/SysRender.h +++ b/src/osp/Active/SysRender.h @@ -31,6 +31,8 @@ namespace osp::active { +using DrawTransforms_t = KeyedVec; + /** * @brief View and Projection matrix */ @@ -62,10 +64,10 @@ struct EntityToDraw * @param UserData_t [in] Non-owning user data */ using ShaderDrawFnc_t = void (*)( - ActiveEnt, ViewProjMatrix const&, UserData_t) noexcept; + DrawEnt, ViewProjMatrix const&, UserData_t) noexcept; constexpr void operator()( - ActiveEnt ent, + DrawEnt ent, ViewProjMatrix const& viewProj) const noexcept { m_draw(ent, viewProj, m_data); @@ -88,7 +90,7 @@ struct EntityToDraw */ struct RenderGroup { - using Storage_t = entt::storage_type::type; + using Storage_t = entt::basic_storage; using ArrayView_t = Corrade::Containers::ArrayView; /** @@ -173,9 +175,10 @@ class SysRender template static void update_draw_transforms( ACtxSceneGraph const& rScnGraph, + KeyedVec const& activeToDraw, acomp_storage_t const& transform, - acomp_storage_t& rDrawTf, - EntSet_t const& rDrawable, + DrawTransforms_t& rDrawTf, + EntSet_t const& useDrawTf, IT_T first, ITB_T const& last); @@ -208,9 +211,10 @@ class SysRender private: static void update_draw_transforms_recurse( ACtxSceneGraph const& rScnGraph, + KeyedVec const& activeToDraw, acomp_storage_t const& rTf, - acomp_storage_t& rDrawTf, - EntSet_t const& rDrawable, + DrawTransforms_t& rDrawTf, + EntSet_t const& useDrawTf, ActiveEnt ent, Matrix4 const& parentTf, bool root); @@ -234,9 +238,10 @@ void SysRender::assure_draw_transforms( template void SysRender::update_draw_transforms( ACtxSceneGraph const& rScnGraph, + KeyedVec const& activeToDraw, acomp_storage_t const& rTf, - acomp_storage_t& rDrawTf, - EntSet_t const& rDrawable, + DrawTransforms_t& rDrawTf, + EntSet_t const& needDrawTf, IT_T first, ITB_T const& last) { @@ -244,7 +249,7 @@ void SysRender::update_draw_transforms( while (first != last) { - update_draw_transforms_recurse(rScnGraph, rTf, rDrawTf, rDrawable, *first, identity, true); + update_draw_transforms_recurse(rScnGraph, activeToDraw, rTf, rDrawTf, needDrawTf, *first, identity, true); std::advance(first, 1); } @@ -253,16 +258,12 @@ void SysRender::update_draw_transforms( template void remove_refcounted( - ActiveEnt const ent, STORAGE_T &rStorage, REFCOUNT_T &rRefcount) + DrawEnt const ent, STORAGE_T &rStorage, REFCOUNT_T &rRefcount) { - if (rStorage.contains(ent)) + auto &rOwner = rStorage[ent]; + if (rOwner.has_value()) { - auto &rOwner = rStorage.get(ent); - if (rOwner.has_value()) - { - rRefcount.ref_release(std::move(rOwner)); - } - rStorage.erase(ent); + rRefcount.ref_release(std::move(rOwner)); } } @@ -272,10 +273,7 @@ void SysRender::update_delete_drawing( { while (first != last) { - ActiveEnt const ent = *first; - rCtxDraw.m_opaque .remove(ent); - rCtxDraw.m_transparent .remove(ent); - rCtxDraw.m_visible .remove(ent); + DrawEnt const ent = rCtxDraw.m_activeToDraw[*first]; // Textures and meshes are reference counted remove_refcounted(ent, rCtxDraw.m_diffuseTex, rCtxDraw.m_texRefCounts); diff --git a/src/osp/Active/activetypes.h b/src/osp/Active/activetypes.h index 30e8a006..45a9286d 100644 --- a/src/osp/Active/activetypes.h +++ b/src/osp/Active/activetypes.h @@ -58,20 +58,11 @@ struct entt::storage_type namespace osp::active { - -inline constexpr unsigned gc_heir_physics_level = 1; - using ActiveReg_t = lgrn::IdRegistryStl; using EntVector_t = std::vector; using EntSet_t = lgrn::BitView< std::vector >; -struct EntSetPair -{ - EntSet_t &m_rEnts; - EntVector_t &m_rDirty; -}; - using active_sparse_set_t = entt::basic_sparse_set; template diff --git a/src/osp/Active/drawing.h b/src/osp/Active/drawing.h index 6497d49c..6db38b62 100644 --- a/src/osp/Active/drawing.h +++ b/src/osp/Active/drawing.h @@ -26,34 +26,34 @@ #include "activetypes.h" #include "basic.h" -#include "../types.h" + #include "../id_map.h" +#include "../keyed_vector.h" +#include "../types.h" #include "../Resource/resourcetypes.h" #include #include -#include #include namespace osp::active { -/** - * @brief An object that is completely opaque - */ -struct ACompOpaque {}; +enum class DrawEnt : uint32_t { }; -/** - * @brief An object with transparency - */ -struct ACompTransparent {}; +struct BasicDrawProps +{ + bool m_opaque:1 { false }; + bool m_transparent:1 { false }; +}; -/** - * @brief Visibility state of this object - */ -struct ACompVisible {}; +struct Material +{ + EntSet_t& m_rEnts; + std::vector& m_rDirty; +}; /** * @brief Mesh that describes the appearance of an entity @@ -69,7 +69,6 @@ enum class MeshId : uint32_t { }; */ enum class TexId : uint32_t { }; -struct ACompColor : Magnum::Color4 {}; using MeshRefCount_t = lgrn::IdRefCount; using MeshIdOwner_t = MeshRefCount_t::Owner_t; @@ -82,28 +81,29 @@ using TexIdOwner_t = TexRefCount_t::Owner_t; */ struct ACtxDrawing { + lgrn::IdRegistryStl m_drawIds; + + EntSet_t m_visible; + EntSet_t m_needDrawTf; + KeyedVec m_drawBasic; + KeyedVec m_color; - // Drawing Components - EntSet_t m_drawable; - acomp_storage_t m_opaque; - acomp_storage_t m_transparent; - acomp_storage_t m_visible; - acomp_storage_t m_color; + KeyedVec m_activeToDraw; // Scene-space Meshes - lgrn::IdRegistry m_meshIds; + lgrn::IdRegistryStl m_meshIds; MeshRefCount_t m_meshRefCounts; // Scene-space Textures - lgrn::IdRegistry m_texIds; + lgrn::IdRegistryStl m_texIds; TexRefCount_t m_texRefCounts; // Meshes and textures assigned to ActiveEnts - acomp_storage_t m_diffuseTex; - std::vector m_diffuseDirty; + KeyedVec m_diffuseTex; + std::vector m_diffuseDirty; - acomp_storage_t m_mesh; - std::vector m_meshDirty; + KeyedVec m_mesh; + std::vector m_meshDirty; }; /** diff --git a/src/osp/Active/opengl/SysRenderGL.cpp b/src/osp/Active/opengl/SysRenderGL.cpp index 3045d4d7..0e8fc8da 100644 --- a/src/osp/Active/opengl/SysRenderGL.cpp +++ b/src/osp/Active/opengl/SysRenderGL.cpp @@ -204,23 +204,20 @@ void SysRenderGL::sync_scene_resources( } void SysRenderGL::assign_meshes( - acomp_storage_t const& cmpMeshIds, - IdMap_t const& meshToRes, - std::vector const& entsDirty, - acomp_storage_t& rCmpMeshGl, - RenderGL& rRenderGl) + KeyedVec const& cmpMeshIds, + IdMap_t const& meshToRes, + std::vector const& entsDirty, + MeshGlEntStorage_t& rCmpMeshGl, + RenderGL& rRenderGl) { - for (ActiveEnt const ent : entsDirty) + for (DrawEnt const ent : entsDirty) { + ACompMeshGl &rEntMeshGl = rCmpMeshGl[ent]; + MeshIdOwner_t const& entMeshScnId = cmpMeshIds[ent]; + // Make sure dirty entity has a MeshId component - if (cmpMeshIds.contains(ent)) + if (entMeshScnId.has_value()) { - MeshId const entMeshScnId = cmpMeshIds.get(ent); - - ACompMeshGl &rEntMeshGl = rCmpMeshGl.contains(ent) - ? rCmpMeshGl.get(ent) - : rCmpMeshGl.emplace(ent); - // Check if scene mesh ID is properly synchronized if (rEntMeshGl.m_scnId == entMeshScnId) { @@ -246,10 +243,10 @@ void SysRenderGL::assign_meshes( } else { - if (rCmpMeshGl.contains(ent)) + if (rEntMeshGl.m_glId != lgrn::id_null()) { // ACompMesh removed, remove ACompMeshGL too - rCmpMeshGl.erase(ent); + rEntMeshGl = {}; } else { @@ -260,22 +257,21 @@ void SysRenderGL::assign_meshes( } void SysRenderGL::assign_textures( - acomp_storage_t const& cmpTexIds, - IdMap_t const& texToRes, - std::vector const& entsDirty, - acomp_storage_t& rCmpTexGl, - RenderGL& rRenderGl) + KeyedVec const& cmpTexIds, + IdMap_t const& texToRes, + std::vector const& entsDirty, + TexGlEntStorage_t& rCmpTexGl, + RenderGL& rRenderGl) { - for (ActiveEnt const ent : entsDirty) + for (DrawEnt const ent : entsDirty) { + ACompTexGl &rEntTexGl = rCmpTexGl[ent]; + TexIdOwner_t const& entTexScnId = cmpTexIds[ent]; + // Make sure dirty entity has a MeshId component - if (cmpTexIds.contains(ent)) + if (entTexScnId.has_value()) { - TexId const entTexScnId = cmpTexIds.get(ent); - ACompTexGl &rEntTexGl = rCmpTexGl.contains(ent) - ? rCmpTexGl.get(ent) - : rCmpTexGl.emplace(ent); // Check if scene mesh ID is properly synchronized if (rEntTexGl.m_scnId == entTexScnId) @@ -302,10 +298,10 @@ void SysRenderGL::assign_textures( } else { - if (rCmpTexGl.contains(ent)) + if (rEntTexGl.m_glId != lgrn::id_null()) { // ACompMesh removed, remove ACompMeshGL too - rCmpTexGl.erase(ent); + rEntTexGl = {}; } else { @@ -352,7 +348,7 @@ void SysRenderGL::clear_resource_owners(RenderGL& rRenderGl, Resources& rResourc void SysRenderGL::render_opaque( RenderGroup const& group, - acomp_storage_t const& visible, + EntSet_t const& visible, ViewProjMatrix const& viewProj) { using Magnum::GL::Renderer; @@ -367,7 +363,7 @@ void SysRenderGL::render_opaque( void SysRenderGL::render_transparent( RenderGroup const& group, - acomp_storage_t const& visible, + EntSet_t const& visible, ViewProjMatrix const& viewProj) { using Magnum::GL::Renderer; @@ -388,12 +384,12 @@ void SysRenderGL::render_transparent( void SysRenderGL::draw_group( RenderGroup const& group, - acomp_storage_t const& visible, + EntSet_t const& visible, ViewProjMatrix const& viewProj) { for (auto const& [ent, toDraw] : group.view().each()) { - if (visible.contains(ent)) + if (visible.test(std::size_t(ent))) { toDraw(ent, viewProj); } diff --git a/src/osp/Active/opengl/SysRenderGL.h b/src/osp/Active/opengl/SysRenderGL.h index 95ca0009..10f21537 100644 --- a/src/osp/Active/opengl/SysRenderGL.h +++ b/src/osp/Active/opengl/SysRenderGL.h @@ -94,17 +94,17 @@ struct ACompMeshGl MeshGlId m_glId {lgrn::id_null()}; }; -using ACompMeshGlStorage_t = acomp_storage_t; -using ACompTexGlStorage_t = acomp_storage_t; +using MeshGlEntStorage_t = KeyedVec; +using TexGlEntStorage_t = KeyedVec; /** * @brief OpenGL specific rendering components for rendering a scene */ struct ACtxSceneRenderGL { - ACompMeshGlStorage_t m_meshId; - ACompTexGlStorage_t m_diffuseTexId; - acomp_storage_t m_drawTransform; + MeshGlEntStorage_t m_meshId; + TexGlEntStorage_t m_diffuseTexId; + DrawTransforms_t m_drawTransform; }; /** @@ -158,11 +158,11 @@ class SysRenderGL * @param rRenderGl [ref] Renderer state */ static void assign_meshes( - acomp_storage_t const& cmpMeshIds, - IdMap_t const& meshToRes, - std::vector const& entsDirty, - acomp_storage_t& rCmpMeshGl, - RenderGL& rRenderGl); + KeyedVec const& cmpMeshIds, + IdMap_t const& meshToRes, + std::vector const& entsDirty, + MeshGlEntStorage_t& rCmpMeshGl, + RenderGL& rRenderGl); /** * @brief Synchronize entities with a TexId component to an ACompTexGl @@ -174,11 +174,11 @@ class SysRenderGL * @param rRenderGl [ref] Renderer state */ static void assign_textures( - acomp_storage_t const& cmpTexIds, - IdMap_t const& texToRes, - std::vector const& entsDirty, - acomp_storage_t& rCmpTexGl, - RenderGL& rRenderGl); + KeyedVec const& cmpTexIds, + IdMap_t const& texToRes, + std::vector const& entsDirty, + TexGlEntStorage_t& rCmpTexGl, + RenderGL& rRenderGl); /** * @brief Call draw functions of a RenderGroup of opaque objects @@ -189,7 +189,7 @@ class SysRenderGL */ static void render_opaque( RenderGroup const& group, - acomp_storage_t const& visible, + EntSet_t const& visible, ViewProjMatrix const& viewProj); /** @@ -203,12 +203,12 @@ class SysRenderGL */ static void render_transparent( RenderGroup const& group, - acomp_storage_t const& visible, + EntSet_t const& visible, ViewProjMatrix const& viewProj); static void draw_group( RenderGroup const& group, - acomp_storage_t const& visible, + EntSet_t const& visible, ViewProjMatrix const& viewProj); template @@ -220,9 +220,7 @@ template void SysRenderGL::update_delete( ACtxSceneRenderGL &rCtxRenderGl, IT_T first, IT_T const& last) { - rCtxRenderGl.m_meshId .remove(first, last); - rCtxRenderGl.m_diffuseTexId .remove(first, last); - rCtxRenderGl.m_drawTransform .remove(first, last); + //rCtxRenderGl.m_drawTransform .remove(first, last); } diff --git a/src/osp/Shaders/Flat.cpp b/src/osp/Shaders/Flat.cpp index f38c3881..6c1a7c25 100644 --- a/src/osp/Shaders/Flat.cpp +++ b/src/osp/Shaders/Flat.cpp @@ -32,7 +32,7 @@ using namespace osp::active; using namespace osp::shader; void shader::draw_ent_flat( - ActiveEnt ent, ViewProjMatrix const& viewProj, + DrawEnt ent, ViewProjMatrix const& viewProj, EntityToDraw::UserData_t userData) noexcept { using Flag = Flat::Flag; @@ -46,22 +46,20 @@ void shader::draw_ent_flat( auto &rShader = *reinterpret_cast(pShader); // Collect uniform information - Matrix4 const &drawTf = rData.m_pDrawTf->get(ent); + Matrix4 const &drawTf = (*rData.m_pDrawTf)[ent]; if (rShader.flags() & Flag::Textured) { - TexGlId const texGlId = rData.m_pDiffuseTexId->get(ent).m_glId; + TexGlId const texGlId = (*rData.m_pDiffuseTexId)[ent].m_glId; rShader.bindTexture(rData.m_pTexGl->get(texGlId)); } if (rData.m_pColor != nullptr) { - rShader.setColor(rData.m_pColor->contains(ent) - ? rData.m_pColor->get(ent) - : 0xffffffff_rgbaf); + rShader.setColor((*rData.m_pColor)[ent]); } - MeshGlId const meshId = rData.m_pMeshId->get(ent).m_glId; + MeshGlId const meshId = (*rData.m_pMeshId)[ent].m_glId; Magnum::GL::Mesh &rMesh = rData.m_pMeshGl->get(meshId); rShader diff --git a/src/osp/Shaders/Flat.h b/src/osp/Shaders/Flat.h index 9c815895..e0468268 100644 --- a/src/osp/Shaders/Flat.h +++ b/src/osp/Shaders/Flat.h @@ -43,10 +43,10 @@ struct ACtxDrawFlat Flat m_shaderUntextured {Corrade::NoCreate}; Flat m_shaderDiffuse {Corrade::NoCreate}; - acomp_storage_t *m_pDrawTf{nullptr}; - acomp_storage_t *m_pColor{nullptr}; - osp::active::ACompTexGlStorage_t *m_pDiffuseTexId{nullptr}; - osp::active::ACompMeshGlStorage_t *m_pMeshId{nullptr}; + osp::active::DrawTransforms_t *m_pDrawTf{nullptr}; + osp::KeyedVec *m_pColor{nullptr}; + osp::active::TexGlEntStorage_t *m_pDiffuseTexId{nullptr}; + osp::active::MeshGlEntStorage_t *m_pMeshId{nullptr}; osp::active::TexGlStorage_t *m_pTexGl{nullptr}; osp::active::MeshGlStorage_t *m_pMeshGl{nullptr}; @@ -65,7 +65,7 @@ struct ACtxDrawFlat }; void draw_ent_flat( - active::ActiveEnt ent, + active::DrawEnt ent, active::ViewProjMatrix const& viewProj, active::EntityToDraw::UserData_t userData) noexcept; @@ -89,15 +89,15 @@ void sync_flat( active::EntSet_t const& hasMaterial, active::RenderGroup::Storage_t *const pStorageOpaque, active::RenderGroup::Storage_t *const pStorageTransparent, - active::acomp_storage_t const& opaque, - active::acomp_storage_t const& diffuse, + KeyedVec const& drawBasic, + KeyedVec const& diffuse, ACtxDrawFlat &rData) { using namespace active; for (; dirtyIt != dirtyLast; std::advance(dirtyIt, 1)) { - ActiveEnt const ent = *dirtyIt; + DrawEnt const ent = *dirtyIt; // Erase from group if they exist if (pStorageOpaque != nullptr) @@ -114,9 +114,9 @@ void sync_flat( continue; // Phong material is not assigned to this entity } - Flat *pShader = diffuse.contains(ent) ? &rData.m_shaderDiffuse : &rData.m_shaderUntextured; + Flat *pShader = diffuse[ent].has_value() ? &rData.m_shaderDiffuse : &rData.m_shaderUntextured; - if (opaque.contains(ent)) + if (drawBasic[ent].m_opaque) { if (pStorageOpaque == nullptr) { diff --git a/src/osp/Shaders/MeshVisualizer.cpp b/src/osp/Shaders/MeshVisualizer.cpp index 804f20e0..cf6cc3aa 100644 --- a/src/osp/Shaders/MeshVisualizer.cpp +++ b/src/osp/Shaders/MeshVisualizer.cpp @@ -36,7 +36,7 @@ using namespace osp::active; using namespace osp::shader; void shader::draw_ent_visualizer( - ActiveEnt ent, const ViewProjMatrix &viewProj, + DrawEnt ent, const ViewProjMatrix &viewProj, EntityToDraw::UserData_t userData) noexcept { using Magnum::Shaders::MeshVisualizerGL3D; @@ -45,7 +45,7 @@ void shader::draw_ent_visualizer( assert(pData != nullptr); auto &rData = *reinterpret_cast(pData); - Matrix4 const& drawTf = rData.m_pDrawTf->get(ent); + Matrix4 const& drawTf = (*rData.m_pDrawTf)[ent]; Matrix4 const entRelative = viewProj.m_view * drawTf; @@ -63,7 +63,7 @@ void shader::draw_ent_visualizer( Magnum::GL::Renderer::setDepthMask(false); } - MeshGlId const meshId = rData.m_pMeshId->get(ent).m_glId; + MeshGlId const meshId = (*rData.m_pMeshId)[ent].m_glId; Magnum::GL::Mesh &rMesh = rData.m_pMeshGl->get(meshId); rShader diff --git a/src/osp/Shaders/MeshVisualizer.h b/src/osp/Shaders/MeshVisualizer.h index 590cadcd..f24247f8 100644 --- a/src/osp/Shaders/MeshVisualizer.h +++ b/src/osp/Shaders/MeshVisualizer.h @@ -37,8 +37,8 @@ struct ACtxDrawMeshVisualizer { MeshVisualizer m_shader{Corrade::NoCreate}; - active::acomp_storage_t *m_pDrawTf{nullptr}; - osp::active::ACompMeshGlStorage_t *m_pMeshId{nullptr}; + osp::active::DrawTransforms_t *m_pDrawTf{nullptr}; + osp::active::MeshGlEntStorage_t *m_pMeshId{nullptr}; osp::active::MeshGlStorage_t *m_pMeshGl{nullptr}; bool m_wireframeOnly{false}; @@ -53,7 +53,7 @@ struct ACtxDrawMeshVisualizer }; void draw_ent_visualizer( - active::ActiveEnt ent, + active::DrawEnt ent, active::ViewProjMatrix const& viewProj, active::EntityToDraw::UserData_t userData) noexcept; @@ -69,7 +69,7 @@ void sync_visualizer( while (dirtyFirst != dirtyLast) { - ActiveEnt const ent = *dirtyFirst; + DrawEnt const ent = *dirtyFirst; bool alreadyAdded = rStorage.contains(ent); if (hasMaterial.test(std::size_t(ent))) { diff --git a/src/osp/Shaders/Phong.cpp b/src/osp/Shaders/Phong.cpp index a410ab60..49316e09 100644 --- a/src/osp/Shaders/Phong.cpp +++ b/src/osp/Shaders/Phong.cpp @@ -32,7 +32,7 @@ using namespace osp::active; using namespace osp::shader; void shader::draw_ent_phong( - ActiveEnt ent, ViewProjMatrix const& viewProj, + DrawEnt ent, ViewProjMatrix const& viewProj, EntityToDraw::UserData_t userData) noexcept { using Flag = Phong::Flag; @@ -46,7 +46,7 @@ void shader::draw_ent_phong( auto &rShader = *reinterpret_cast(pShader); // Collect uniform information - Matrix4 const &drawTf = rData.m_pDrawTf->get(ent); + Matrix4 const &drawTf = (*rData.m_pDrawTf)[ent]; Magnum::Matrix4 entRelative = viewProj.m_view * drawTf; @@ -58,7 +58,7 @@ void shader::draw_ent_phong( if (rShader.flags() & Flag::DiffuseTexture) { - TexGlId const texGlId = rData.m_pDiffuseTexId->get(ent).m_glId; + TexGlId const texGlId = (*rData.m_pDiffuseTexId)[ent].m_glId; Magnum::GL::Texture2D &rTexture = rData.m_pTexGl->get(texGlId); rShader.bindDiffuseTexture(rTexture); @@ -70,12 +70,10 @@ void shader::draw_ent_phong( if (rData.m_pColor != nullptr) { - rShader.setDiffuseColor(rData.m_pColor->contains(ent) - ? rData.m_pColor->get(ent) - : 0xffffffff_rgbaf); + rShader.setDiffuseColor((*rData.m_pColor)[ent]); } - MeshGlId const meshId = rData.m_pMeshId->get(ent).m_glId; + MeshGlId const meshId = (*rData.m_pMeshId)[ent].m_glId; Magnum::GL::Mesh &rMesh = rData.m_pMeshGl->get(meshId); Matrix3 a{viewProj.m_view}; diff --git a/src/osp/Shaders/Phong.h b/src/osp/Shaders/Phong.h index d28a2bf1..aff04173 100644 --- a/src/osp/Shaders/Phong.h +++ b/src/osp/Shaders/Phong.h @@ -44,10 +44,10 @@ struct ACtxDrawPhong Phong m_shaderUntextured {Corrade::NoCreate}; Phong m_shaderDiffuse {Corrade::NoCreate}; - acomp_storage_t *m_pDrawTf{nullptr}; - acomp_storage_t *m_pColor{nullptr}; - osp::active::ACompTexGlStorage_t *m_pDiffuseTexId{nullptr}; - osp::active::ACompMeshGlStorage_t *m_pMeshId{nullptr}; + osp::active::DrawTransforms_t *m_pDrawTf{nullptr}; + osp::KeyedVec *m_pColor{nullptr}; + osp::active::TexGlEntStorage_t *m_pDiffuseTexId{nullptr}; + osp::active::MeshGlEntStorage_t *m_pMeshId{nullptr}; osp::active::TexGlStorage_t *m_pTexGl{nullptr}; osp::active::MeshGlStorage_t *m_pMeshGl{nullptr}; @@ -66,7 +66,7 @@ struct ACtxDrawPhong }; void draw_ent_phong( - active::ActiveEnt ent, + active::DrawEnt ent, active::ViewProjMatrix const& viewProj, active::EntityToDraw::UserData_t userData) noexcept; @@ -90,15 +90,15 @@ void sync_phong( active::EntSet_t const& hasMaterial, active::RenderGroup::Storage_t *const pStorageOpaque, active::RenderGroup::Storage_t *const pStorageTransparent, - active::acomp_storage_t const& opaque, - active::acomp_storage_t const& diffuse, + KeyedVec const& drawBasic, + active::TexGlEntStorage_t const& diffuse, ACtxDrawPhong &rData) { using namespace active; for (; dirtyIt != dirtyLast; std::advance(dirtyIt, 1)) { - ActiveEnt const ent = *dirtyIt; + DrawEnt const ent = *dirtyIt; // Erase from group if they exist if (pStorageOpaque != nullptr) @@ -115,9 +115,11 @@ void sync_phong( continue; // Phong material is not assigned to this entity } - Phong *pShader = diffuse.contains(ent) ? &rData.m_shaderDiffuse : &rData.m_shaderUntextured; + Phong *pShader = (diffuse[ent].m_glId != lgrn::id_null()) + ? &rData.m_shaderDiffuse + : &rData.m_shaderUntextured; - if (opaque.contains(ent)) + if (drawBasic[ent].m_opaque) { if (pStorageOpaque == nullptr) { diff --git a/src/osp/keyed_bitview.h b/src/osp/keyed_bitview.h new file mode 100644 index 00000000..e69de29b diff --git a/src/osp/vecmap.h b/src/osp/vecmap.h new file mode 100644 index 00000000..e69de29b From 66805908e0e7ac0f5432b077f085d7c81be3d387 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sat, 15 Apr 2023 19:40:08 -0700 Subject: [PATCH 09/35] Fix code after adding "Draw Entities" --- src/osp/Active/SysPrefabInit.cpp | 62 +++++++++++--- src/osp/Active/SysRender.cpp | 4 +- src/osp/Active/SysRender.h | 16 ++-- src/osp/Active/activetypes.h | 8 +- src/osp/Active/drawing.h | 24 +++++- src/osp/Active/opengl/SysRenderGL.cpp | 2 - src/osp/Shaders/Flat.h | 6 +- src/osp/keyed_bitview.h | 33 ++++++++ src/osp/keyed_vector.h | 82 +++++++++++++++++++ src/osp/vecmap.h | 0 .../activescenes/identifiers.h | 7 +- .../activescenes/scenarios_enginetest.cpp | 54 ++++++------ .../activescenes/scene_common.cpp | 70 +++++++++++++--- .../activescenes/scene_misc.cpp | 31 +++---- .../activescenes/scene_physics.cpp | 40 +++++---- .../activescenes/scene_renderer.cpp | 73 +++++++++-------- 16 files changed, 379 insertions(+), 133 deletions(-) create mode 100644 src/osp/keyed_vector.h delete mode 100644 src/osp/vecmap.h diff --git a/src/osp/Active/SysPrefabInit.cpp b/src/osp/Active/SysPrefabInit.cpp index 91f652fc..ecba202b 100644 --- a/src/osp/Active/SysPrefabInit.cpp +++ b/src/osp/Active/SysPrefabInit.cpp @@ -118,8 +118,10 @@ void SysPrefabInit::init_drawing( ACtxDrawingRes& rDrawingRes, std::optional material) noexcept { - auto itPfEnts = std::begin(rPrefabInit.m_ents); + auto itPfEnts = rPrefabInit.m_ents.begin(); + // stupidly make drawIDs first + // TODO: separate this into another step. for (TmpPrefabInitBasic const& rPfBasic : rPrefabInit.m_basicIn) { auto const &rImportData = rResources.data_get( @@ -131,13 +133,53 @@ void SysPrefabInit::init_drawing( for (std::size_t i = 0; i < objects.size(); ++i) { + int const meshImportId = rImportData.m_objMeshes[objects[i]]; + if (meshImportId == -1) + { + continue; + } + ActiveEnt const ent = (*itPfEnts)[i]; + rDrawing.m_activeToDraw[ent] = rDrawing.m_drawIds.create(); + } + + ++itPfEnts; + } + + // then resize containers + rDrawing.resize_draw(); + + itPfEnts = rPrefabInit.m_ents.begin(); + + for (TmpPrefabInitBasic const& rPfBasic : rPrefabInit.m_basicIn) + { + auto const &rImportData = rResources.data_get( + gc_importer, rPfBasic.m_importerRes); + auto const &rPrefabData = rResources.data_get( + gc_importer, rPfBasic.m_importerRes); - // TODO: Don't actually do this. This marks every entity as drawable, - // which considers them for draw transformations. - // Only set drawable for entities that have a mesh or is an - // ancestor of an entity with a mesh. - //rDrawing.m_drawable.set(std::size_t(ent)); + auto const ents = ArrayView{*itPfEnts}; + auto const objects = lgrn::Span{rPrefabData.m_prefabs[rPfBasic.m_prefabId]}; + auto const parents = lgrn::Span{rPrefabData.m_prefabParents[rPfBasic.m_prefabId]}; + + // All ancestors of each entity that has a mesh + auto const needs_draw_transform + = [&parents, &ents, &rDrawing, &needDrawTf = rDrawing.m_needDrawTf] + (auto && self, int const object, ActiveEnt const ent) noexcept -> void + { + needDrawTf.set(std::size_t(ent)); + + int const parentObj = parents[object]; + + if (parentObj != -1) + { + self(self, parentObj, ents[parentObj]); + } + }; + + for (std::size_t i = 0; i < objects.size(); ++i) + { + ActiveEnt const ent = (*itPfEnts)[i]; // Check if object has mesh int const meshImportId = rImportData.m_objMeshes[objects[i]]; @@ -146,8 +188,9 @@ void SysPrefabInit::init_drawing( continue; } - DrawEnt const drawEnt = rDrawing.m_drawIds.create(); - rDrawing.m_activeToDraw[ent] = drawEnt; + needs_draw_transform(needs_draw_transform, objects[i], ent); + + DrawEnt const drawEnt = rDrawing.m_activeToDraw[ent]; osp::ResId const meshRes = rImportData.m_meshes[meshImportId]; MeshId const meshId = SysRender::own_mesh_resource(rDrawing, rDrawingRes, rResources, meshRes); @@ -168,7 +211,6 @@ void SysPrefabInit::init_drawing( rDrawing.m_diffuseTex[drawEnt] = rDrawing.m_texRefCounts.ref_add(texId); rDrawing.m_diffuseDirty.push_back(drawEnt); } - } rDrawing.m_drawBasic[drawEnt] = { .m_opaque = true, .m_transparent = false }; @@ -181,7 +223,7 @@ void SysPrefabInit::init_drawing( } } - std::advance(itPfEnts, 1); + ++itPfEnts; } } diff --git a/src/osp/Active/SysRender.cpp b/src/osp/Active/SysRender.cpp index 7f870fc7..0085605c 100644 --- a/src/osp/Active/SysRender.cpp +++ b/src/osp/Active/SysRender.cpp @@ -127,8 +127,8 @@ void SysRender::update_draw_transforms_recurse( Matrix4 const& parentTf, bool root) { - Matrix4 const& entTf = rTf.get(ent).m_transform; - Matrix4 const& entDrawTf = root ? (entTf) : (parentTf * entTf); + Matrix4 const& entTf = rTf.get(ent).m_transform; + Matrix4 const& entDrawTf = root ? (entTf) : (parentTf * entTf); if (DrawEnt const drawEnt = activeToDraw[ent]; drawEnt != lgrn::id_null()) diff --git a/src/osp/Active/SysRender.h b/src/osp/Active/SysRender.h index 7bf24384..df00c47f 100644 --- a/src/osp/Active/SysRender.h +++ b/src/osp/Active/SysRender.h @@ -198,7 +198,7 @@ class SysRender template static void update_delete_drawing( - ACtxDrawing& rCtxDraw, IT_T first, IT_T const& last); + ACtxDrawing& rCtxDraw, IT_T const& first, IT_T const& last); template static void update_delete_groups( @@ -269,18 +269,14 @@ void remove_refcounted( template void SysRender::update_delete_drawing( - ACtxDrawing& rCtxDraw, IT_T first, IT_T const& last) + ACtxDrawing& rCtxDraw, IT_T const& first, IT_T const& last) { - while (first != last) + for (auto it = first; it != last; std::advance(it, 1)) { - DrawEnt const ent = rCtxDraw.m_activeToDraw[*first]; - - // Textures and meshes are reference counted - remove_refcounted(ent, rCtxDraw.m_diffuseTex, rCtxDraw.m_texRefCounts); - remove_refcounted(ent, rCtxDraw.m_mesh, rCtxDraw.m_meshRefCounts); + DrawEnt const drawEnt = *it; - - std::advance(first, 1); + remove_refcounted(drawEnt, rCtxDraw.m_diffuseTex, rCtxDraw.m_texRefCounts); + remove_refcounted(drawEnt, rCtxDraw.m_mesh, rCtxDraw.m_meshRefCounts); } } diff --git a/src/osp/Active/activetypes.h b/src/osp/Active/activetypes.h index 45a9286d..e27d649c 100644 --- a/src/osp/Active/activetypes.h +++ b/src/osp/Active/activetypes.h @@ -24,6 +24,8 @@ */ #pragma once +#include "../bitvector.h" // for osp::BitVector_t + // IWYU pragma: begin_exports #include // for entt::id_type @@ -58,10 +60,10 @@ struct entt::storage_type namespace osp::active { -using ActiveReg_t = lgrn::IdRegistryStl; +using ActiveReg_t = lgrn::IdRegistryStl; -using EntVector_t = std::vector; -using EntSet_t = lgrn::BitView< std::vector >; +using EntVector_t = std::vector; +using EntSet_t = BitVector_t; using active_sparse_set_t = entt::basic_sparse_set; diff --git a/src/osp/Active/drawing.h b/src/osp/Active/drawing.h index 6db38b62..13b9a832 100644 --- a/src/osp/Active/drawing.h +++ b/src/osp/Active/drawing.h @@ -27,6 +27,7 @@ #include "activetypes.h" #include "basic.h" +#include "../bitvector.h" #include "../id_map.h" #include "../keyed_vector.h" #include "../types.h" @@ -43,6 +44,9 @@ namespace osp::active enum class DrawEnt : uint32_t { }; +using DrawEntVector_t = std::vector; +using DrawEntSet_t = BitVector_t; + struct BasicDrawProps { bool m_opaque:1 { false }; @@ -81,13 +85,31 @@ using TexIdOwner_t = TexRefCount_t::Owner_t; */ struct ACtxDrawing { + + void resize_draw() + { + std::size_t const size = m_drawIds.capacity(); + + bitvector_resize(m_visible, size); + m_drawBasic .resize(size); + m_color .resize(size); + m_diffuseTex .resize(size); + m_mesh .resize(size); + } + + void resize_active(std::size_t const size) + { + bitvector_resize(m_needDrawTf, size); + m_activeToDraw.resize(size, lgrn::id_null()); + } + lgrn::IdRegistryStl m_drawIds; EntSet_t m_visible; - EntSet_t m_needDrawTf; KeyedVec m_drawBasic; KeyedVec m_color; + EntSet_t m_needDrawTf; KeyedVec m_activeToDraw; // Scene-space Meshes diff --git a/src/osp/Active/opengl/SysRenderGL.cpp b/src/osp/Active/opengl/SysRenderGL.cpp index 0e8fc8da..17e58765 100644 --- a/src/osp/Active/opengl/SysRenderGL.cpp +++ b/src/osp/Active/opengl/SysRenderGL.cpp @@ -271,8 +271,6 @@ void SysRenderGL::assign_textures( // Make sure dirty entity has a MeshId component if (entTexScnId.has_value()) { - - // Check if scene mesh ID is properly synchronized if (rEntTexGl.m_scnId == entTexScnId) { diff --git a/src/osp/Shaders/Flat.h b/src/osp/Shaders/Flat.h index e0468268..cecb897f 100644 --- a/src/osp/Shaders/Flat.h +++ b/src/osp/Shaders/Flat.h @@ -90,7 +90,7 @@ void sync_flat( active::RenderGroup::Storage_t *const pStorageOpaque, active::RenderGroup::Storage_t *const pStorageTransparent, KeyedVec const& drawBasic, - KeyedVec const& diffuse, + active::TexGlEntStorage_t const& diffuse, ACtxDrawFlat &rData) { using namespace active; @@ -114,7 +114,9 @@ void sync_flat( continue; // Phong material is not assigned to this entity } - Flat *pShader = diffuse[ent].has_value() ? &rData.m_shaderDiffuse : &rData.m_shaderUntextured; + Flat *pShader = (diffuse[ent].m_glId != lgrn::id_null()) + ? &rData.m_shaderDiffuse + : &rData.m_shaderUntextured; if (drawBasic[ent].m_opaque) { diff --git a/src/osp/keyed_bitview.h b/src/osp/keyed_bitview.h index e69de29b..54d18d02 100644 --- a/src/osp/keyed_bitview.h +++ b/src/osp/keyed_bitview.h @@ -0,0 +1,33 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * 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 + +namespace osp +{ + + + +} // namespace osp diff --git a/src/osp/keyed_vector.h b/src/osp/keyed_vector.h new file mode 100644 index 00000000..22230919 --- /dev/null +++ b/src/osp/keyed_vector.h @@ -0,0 +1,82 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * 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 + +#include + +namespace osp +{ + +/** + * @brief Wraps an std::vector intended to be accessed using a (strong typedef) enum class ID + */ +template > +class KeyedVec : public std::vector +{ + using vector_t = std::vector; + using int_t = lgrn::underlying_int_type_t; + +public: + + using value_type = typename vector_t::value_type; + using allocator_type = typename vector_t::allocator_type; + using reference = typename vector_t::reference; + using const_reference = typename vector_t::const_reference; + using pointer = typename vector_t::pointer; + using const_pointer = typename vector_t::const_pointer; + using iterator = typename vector_t::iterator; + using const_iterator = typename vector_t::const_iterator; + using reverse_iterator = typename vector_t::reverse_iterator; + using const_reverse_iterator = typename vector_t::const_reverse_iterator; + using difference_type = typename vector_t::difference_type; + using size_type = typename vector_t::size_type; + + reference at(ID_T const id) + { + return vector_t::at(std::size_t(id)); + } + + const_reference at(ID_T const id) const + { + return vector_t::at(std::size_t(id)); + } + + reference operator[] (ID_T const id) + { + return vector_t::operator[](std::size_t(id)); + } + + const_reference operator[] (ID_T const id) const + { + return vector_t::operator[](std::size_t(id)); + } + +}; // class KeyedVec + + + +} // namespace osp diff --git a/src/osp/vecmap.h b/src/osp/vecmap.h deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 89cb0b28..6e88b61c 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -45,9 +45,9 @@ // Scene sessions -#define OSP_DATA_TESTAPP_COMMON_SCENE 8, \ - idDeltaTimeIn, idActiveIds, idBasic, idDrawing, idDrawingRes, idDelEnts, idDelTotal, idNMesh -#define OSP_TAGS_TESTAPP_COMMON_SCENE 34, \ +#define OSP_DATA_TESTAPP_COMMON_SCENE 9, \ + idDeltaTimeIn, idActiveIds, idBasic, idDrawing, idDrawingRes, idDelEnts, idDelTotal, idDelDrawEnts, idNMesh +#define OSP_TAGS_TESTAPP_COMMON_SCENE 37, \ tgCleanupEvt, tgResyncEvt, tgSyncEvt, tgSceneEvt, tgTimeEvt, \ tgEntDel, tgEntNew, tgEntReq, \ tgDelEntMod, tgDelEntReq, tgDelEntClr, \ @@ -55,6 +55,7 @@ tgTransformMod, tgTransformDel, tgTransformNew, tgTransformReq, \ tgHierMod, tgHierModEnd, tgHierDel, tgHierNew, tgHierReq, \ tgDrawDel, tgDrawMod, tgDrawReq, \ + tgDelDrawEntMod, tgDelDrawEntReq, tgDelDrawEntClr, \ tgMeshDel, tgMeshMod, tgMeshReq, tgMeshClr, \ tgTexDel, tgTexMod, tgTexReq, tgTexClr diff --git a/src/test_application/activescenes/scenarios_enginetest.cpp b/src/test_application/activescenes/scenarios_enginetest.cpp index 897e71b6..23e1e82e 100644 --- a/src/test_application/activescenes/scenarios_enginetest.cpp +++ b/src/test_application/activescenes/scenarios_enginetest.cpp @@ -96,8 +96,9 @@ struct EngineTestScene ActiveEnt m_cube{lgrn::id_null()}; // Set of ActiveEnts that are assigned a Phong material - osp::active::EntSet_t m_matPhong; - osp::active::EntVector_t m_matPhongDirty; + osp::active::EntSet_t m_matPhong; + std::vector m_matPhongDirty; + }; EngineTestScene::~EngineTestScene() @@ -124,12 +125,14 @@ entt::any setup_scene(osp::Resources& rResources, osp::PkgId const pkg) rScene.m_pResources = &rResources; // Make a cube - rScene.m_cube = rScene.m_activeIds.create(); + ActiveEnt const cubeEnt = rScene.m_activeIds.create(); + DrawEnt const cubeDraw = rScene.m_drawing.m_drawIds.create(); // Resize some containers to fit all existing entities - rScene.m_matPhong.ints().resize(rScene.m_activeIds.vec().capacity()); - rScene.m_drawing.m_drawable.ints().resize(rScene.m_activeIds.vec().capacity()); - rScene.m_basic.m_scnGraph.resize(rScene.m_activeIds.capacity()); + rScene.m_matPhong.ints() .resize(rScene.m_activeIds.vec().capacity()); + rScene.m_basic.m_scnGraph .resize(rScene.m_activeIds.capacity()); + rScene.m_drawing.resize_active(rScene.m_activeIds.capacity()); + rScene.m_drawing.resize_draw(); // Take ownership of the cube mesh Resource. This will create a scene-space // MeshId that we can assign to ActiveEnts @@ -138,25 +141,26 @@ entt::any setup_scene(osp::Resources& rResources, osp::PkgId const pkg) MeshId const meshCube = SysRender::own_mesh_resource(rScene.m_drawing, rScene.m_drawingRes, rResources, resCube); // Add cube mesh to cube - rScene.m_drawing.m_mesh.emplace( - rScene.m_cube, rScene.m_drawing.m_meshRefCounts.ref_add(meshCube)); - rScene.m_drawing.m_meshDirty.push_back(rScene.m_cube); + + rScene.m_drawing.m_mesh[cubeDraw] = rScene.m_drawing.m_meshRefCounts.ref_add(meshCube); + rScene.m_drawing.m_meshDirty.push_back(cubeDraw); // Add transform - rScene.m_basic.m_transform.emplace(rScene.m_cube); + rScene.m_basic.m_transform.emplace(cubeEnt); // Add phong material to cube - rScene.m_matPhong.set(std::size_t(rScene.m_cube)); - rScene.m_matPhongDirty.push_back(rScene.m_cube); + rScene.m_matPhong.set(std::size_t(cubeDraw)); + rScene.m_matPhongDirty.push_back(cubeDraw); // Add drawble, opaque, and visible component - rScene.m_drawing.m_drawable.set(std::size_t(rScene.m_cube)); - rScene.m_drawing.m_opaque.emplace(rScene.m_cube); - rScene.m_drawing.m_visible.emplace(rScene.m_cube); + rScene.m_drawing.m_visible.set(std::size_t(cubeDraw)); + rScene.m_drawing.m_drawBasic[cubeDraw].m_opaque = true; // Add cube to hierarchy, parented to root SubtreeBuilder builder = SysSceneGraph::add_descendants(rScene.m_basic.m_scnGraph, 1); - builder.add_child(rScene.m_cube); + builder.add_child(cubeEnt); + + rScene.m_cube = cubeEnt; return sceneAny; } @@ -225,19 +229,18 @@ void sync_test_scene( using namespace osp::active; using namespace osp::shader; + rRenderer.m_renderGl.m_drawTransform.resize(rScene.m_drawing.m_drawIds.capacity()); + rRenderer.m_renderGl.m_diffuseTexId.resize(rScene.m_drawing.m_drawIds.capacity()); + rRenderer.m_renderGl.m_meshId.resize(rScene.m_drawing.m_drawIds.capacity()); + // Assign or remove phong shaders from entities marked dirty sync_phong( std::cbegin(rScene.m_matPhongDirty), std::cend(rScene.m_matPhongDirty), rScene.m_matPhong, &rRenderer.m_groupFwdOpaque.m_entities, nullptr, - rScene.m_drawing.m_opaque, rRenderer.m_renderGl.m_diffuseTexId, + rScene.m_drawing.m_drawBasic, rRenderer.m_renderGl.m_diffuseTexId, rRenderer.m_phong); - SysRender::assure_draw_transforms( - rRenderer.m_renderGl.m_drawTransform, - std::cbegin(rScene.m_matPhongDirty), - std::cend(rScene.m_matPhongDirty)); - // Load required meshes and textures into OpenGL SysRenderGL::sync_scene_resources(rScene.m_drawingRes, *rScene.m_pResources, rRenderGl); @@ -257,9 +260,12 @@ void sync_test_scene( SysRender::update_draw_transforms( rScene.m_basic.m_scnGraph, + rScene.m_drawing.m_activeToDraw, rScene.m_basic.m_transform, rRenderer.m_renderGl.m_drawTransform, - rScene.m_drawing.m_drawable, drawTfDirty.begin(), drawTfDirty.end()); + rScene.m_drawing.m_needDrawTf, + drawTfDirty.begin(), + drawTfDirty.end()); } /** @@ -327,7 +333,7 @@ on_draw_t generate_draw_func(EngineTestScene& rScene, ActiveApplication &rApp, R SysRender::set_dirty_all(rScene.m_drawing); for (std::size_t const entInt : rScene.m_matPhong.ones()) { - rScene.m_matPhongDirty.push_back(ActiveEnt(entInt)); + rScene.m_matPhongDirty.push_back(DrawEnt(entInt)); } sync_test_scene(rRenderGl, rScene, *pRenderer); diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index cf90e467..5dbe4cc7 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -59,6 +59,7 @@ Session setup_common_scene( top_emplace< float > (topData, idDeltaTimeIn, 1.0f / 60.0f); top_emplace< EntVector_t > (topData, idDelEnts); top_emplace< EntVector_t > (topData, idDelTotal); + top_emplace< DrawEntVector_t > (topData, idDelDrawEnts); auto &rBasic = top_emplace< ACtxBasic > (topData, idBasic); auto &rActiveIds = top_emplace< ActiveReg_t > (topData, idActiveIds); @@ -80,6 +81,8 @@ Session setup_common_scene( rBuilder.tag(tgHierReq) .depend_on({tgHierMod, tgHierModEnd}); rBuilder.tag(tgDrawMod) .depend_on({tgDrawDel}); rBuilder.tag(tgDrawReq) .depend_on({tgDrawDel, tgDrawMod}); + rBuilder.tag(tgDelDrawEntReq) .depend_on({tgDelDrawEntMod}); + rBuilder.tag(tgDelDrawEntClr) .depend_on({tgDelDrawEntMod, tgDelDrawEntReq}); rBuilder.tag(tgMeshMod) .depend_on({tgMeshDel}); rBuilder.tag(tgMeshReq) .depend_on({tgMeshDel, tgMeshMod}); rBuilder.tag(tgMeshClr) .depend_on({tgMeshDel, tgMeshMod, tgMeshReq}); @@ -148,19 +151,49 @@ Session setup_common_scene( })); scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgDrawDel}).data( + "Delete DrawEntity of deleted ActiveEnts", + TopDataIds_t{ idDrawing, idDelTotal, idDelDrawEnts}, + wrap_args([] (ACtxDrawing& rDrawing, EntVector_t const& rDelTotal, DrawEntVector_t& rDelDrawEnts) noexcept + { + for (ActiveEnt const ent : rDelTotal) + { + DrawEnt const drawEnt = std::exchange(rDrawing.m_activeToDraw[ent], lgrn::id_null()); + if (drawEnt != lgrn::id_null()) + { + rDelDrawEnts.push_back(drawEnt); + } + } + })); + + scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelDrawEntReq, tgDrawDel}).data( "Delete drawing components", - TopDataIds_t{ idDrawing, idDelTotal}, - wrap_args([] (ACtxDrawing& rDrawing, EntVector_t const& rDelTotal) noexcept + TopDataIds_t{ idDrawing, idDelDrawEnts}, + wrap_args([] (ACtxDrawing& rDrawing, DrawEntVector_t const& rDelDrawEnts) noexcept + { + SysRender::update_delete_drawing(rDrawing, rDelDrawEnts.cbegin(), rDelDrawEnts.cend()); + })); + + scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelDrawEntReq}).data( + "Delete DrawEntity IDs", + TopDataIds_t{ idDrawing, idDelDrawEnts}, + wrap_args([] (ACtxDrawing& rDrawing, DrawEntVector_t const& rDelDrawEnts) noexcept { - SysRender::update_delete_drawing(rDrawing, std::cbegin(rDelTotal), std::cend(rDelTotal)); + for (DrawEnt const drawEnt : rDelDrawEnts) + { + if (rDrawing.m_drawIds.exists(drawEnt)) + { + rDrawing.m_drawIds.remove(drawEnt); + } + } })); - scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelEntClr}).data( + scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelEntClr, tgDelDrawEntClr}).data( "Clear delete vectors once we're done with it", - TopDataIds_t{ idDelEnts}, - wrap_args([] (EntVector_t& rDelEnts) noexcept + TopDataIds_t{ idDelEnts, idDelDrawEnts}, + wrap_args([] (EntVector_t& rDelEnts, DrawEntVector_t& rDelDrawEnts) noexcept { rDelEnts.clear(); + rDelDrawEnts.clear(); })); scnCommon.task() = rBuilder.task().assign({tgCleanupEvt}).data( @@ -216,8 +249,8 @@ Session setup_material( OSP_SESSION_ACQUIRE_DATA(material, topData, TESTAPP_MATERIAL); OSP_SESSION_ACQUIRE_TAGS(material, rTags, TESTAPP_MATERIAL); - top_emplace< EntSet_t > (topData, idMatEnts); - top_emplace< EntVector_t > (topData, idMatDirty); + top_emplace< EntSet_t > (topData, idMatEnts); + top_emplace< std::vector > (topData, idMatDirty); rBuilder.tag(tgMatMod) .depend_on({tgMatDel}); rBuilder.tag(tgMatReq) .depend_on({tgMatDel, tgMatMod}); @@ -226,26 +259,37 @@ Session setup_material( material.task() = rBuilder.task().assign({tgResyncEvt}).data( "Set all X material entities as dirty", TopDataIds_t{ idMatEnts, idMatDirty}, - wrap_args([] (EntSet_t const& rMatEnts, EntVector_t& rMatDirty) noexcept + wrap_args([] (EntSet_t const& rMatEnts, std::vector& rMatDirty) noexcept { for (std::size_t const entInt : rMatEnts.ones()) { - rMatDirty.push_back(ActiveEnt(entInt)); + rMatDirty.push_back(DrawEnt(entInt)); } })); material.task() = rBuilder.task().assign({tgSceneEvt, tgSyncEvt, tgMatClr}).data( "Clear dirty vectors for material", - TopDataIds_t{ idMatDirty}, - wrap_args([] (EntVector_t& rMatDirty) noexcept + TopDataIds_t{ idMatDirty}, + wrap_args([] (std::vector& rMatDirty) noexcept { rMatDirty.clear(); })); material.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgMatDel}).data( "Delete material components", - TopDataIds_t{idMatEnts, idDelTotal}, delete_ent_set); + TopDataIds_t{ idDrawing, idDelTotal, idMatEnts}, + wrap_args([] (ACtxDrawing const& rDrawing, EntVector_t const& rDelTotal, EntSet_t& rMatEnts) noexcept + { + for (ActiveEnt const ent : rDelTotal) + { + DrawEnt const drawEnt = rDrawing.m_activeToDraw[ent]; + if (drawEnt != lgrn::id_null()) + { + rMatEnts.reset(std::size_t(drawEnt)); + } + } + })); return material; } diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index 5e66f59f..b2ca709e 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -69,7 +69,7 @@ void add_floor( auto &rDrawing = top_get< ACtxDrawing > (topData, idDrawing); auto &rDrawingRes = top_get< ACtxDrawingRes > (topData, idDrawingRes); auto &rMatEnts = top_get< EntSet_t > (topData, idMatEnts); - auto &rMatDirty = top_get< EntVector_t > (topData, idMatDirty); + auto &rMatDirty = top_get< std::vector > (topData, idMatDirty); auto &rSpawner = top_get< SpawnerVec_t > (topData, idSpawner); // Convenient functor to get a reference-counted mesh owner @@ -83,31 +83,32 @@ void add_floor( // Create floor root and mesh entity ActiveEnt const floorRootEnt = rActiveIds.create(); ActiveEnt const floorMeshEnt = rActiveIds.create(); + DrawEnt const floorMeshDrawEnt = rDrawing.m_drawIds.create(); // Resize some containers to fit all existing entities - rDrawing.m_drawable.ints().resize(rActiveIds.vec().capacity()); rBasic.m_scnGraph.resize(rActiveIds.capacity()); + rDrawing.resize_active(rActiveIds.capacity()); + rDrawing.resize_draw(); + bitvector_resize(rMatEnts, rDrawing.m_drawIds.capacity()); - // Add transform and draw transform to root rBasic.m_transform.emplace(floorRootEnt); // Add mesh to floor mesh entity - rDrawing.m_mesh.emplace(floorMeshEnt, quick_add_mesh("grid64solid")); - rDrawing.m_meshDirty.push_back(floorMeshEnt); + rDrawing.m_activeToDraw[floorMeshEnt] = floorMeshDrawEnt; + rDrawing.m_mesh[floorMeshDrawEnt] = quick_add_mesh("grid64solid"); + rDrawing.m_meshDirty.push_back(floorMeshDrawEnt); // Add mesh visualizer material to floor mesh entity - rMatEnts.ints().resize(rActiveIds.vec().capacity()); - rMatEnts.set(std::size_t(floorMeshEnt)); - rMatDirty.push_back(floorMeshEnt); - // Add transform, draw transform, opaque, and visible entity - rBasic.m_transform.emplace( - floorMeshEnt, ACompTransform{Matrix4::scaling(sc_floorSize)}); - rDrawing.m_opaque.emplace(floorMeshEnt); - rDrawing.m_visible.emplace(floorMeshEnt); + rMatEnts.set(std::size_t(floorMeshDrawEnt)); + rMatDirty.push_back(floorMeshDrawEnt); - rDrawing.m_drawable.set(std::size_t(floorRootEnt)); - rDrawing.m_drawable.set(std::size_t(floorMeshEnt)); + // Add transform, draw transform, opaque, and visible entity + rBasic.m_transform.emplace(floorMeshEnt, ACompTransform{Matrix4::scaling(sc_floorSize)}); + rDrawing.m_drawBasic[floorMeshDrawEnt].m_opaque = true; + rDrawing.m_visible.set(std::size_t(floorMeshDrawEnt)); + rDrawing.m_needDrawTf.set(std::size_t(floorRootEnt)); + rDrawing.m_needDrawTf.set(std::size_t(floorMeshEnt)); SubtreeBuilder builder = SysSceneGraph::add_descendants(rBasic.m_scnGraph, 2); diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index 1a9f18b9..d9e090a3 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -155,33 +155,43 @@ Session setup_shape_spawn( shapeSpawn.task() = rBuilder.task().assign({tgSceneEvt, tgSpawnReq, tgSpawnEntReq, tgMeshMod, tgDrawMod, tgMatMod}).data( "Add mesh and material to spawned shapes", - TopDataIds_t{ idDrawing, idSpawner, idSpawnerEnts, idNMesh, idMatEnts, idMatDirty, idActiveIds}, - wrap_args([] (ACtxDrawing& rDrawing, SpawnerVec_t& rSpawner, EntVector_t& rSpawnerEnts, NamedMeshes& rNMesh, EntSet_t& rMatEnts, EntVector_t& rMatDirty, ActiveReg_t const& rActiveIds ) noexcept + TopDataIds_t{ idDrawing, idSpawner, idSpawnerEnts, idNMesh, idMatEnts, idMatDirty, idActiveIds}, + wrap_args([] (ACtxDrawing& rDrawing, SpawnerVec_t& rSpawner, EntVector_t& rSpawnerEnts, NamedMeshes& rNMesh, EntSet_t& rMatEnts, std::vector& rMatDirty, ActiveReg_t const& rActiveIds ) noexcept { if (rSpawner.size() == 0) { return; } - rMatEnts.ints().resize(rActiveIds.vec().capacity()); - rDrawing.m_drawable.ints().resize(rActiveIds.vec().capacity()); + + rDrawing.resize_active(rActiveIds.capacity()); + + for (std::size_t i = 0; i < rSpawner.size(); ++i) + { + ActiveEnt const child = rSpawnerEnts[i * 2 + 1]; + rDrawing.m_activeToDraw[child] = rDrawing.m_drawIds.create(); + } + + rDrawing.resize_draw(); + bitvector_resize(rMatEnts, rDrawing.m_drawIds.capacity()); for (std::size_t i = 0; i < rSpawner.size(); ++i) { SpawnShape const &spawn = rSpawner[i]; ActiveEnt const root = rSpawnerEnts[i * 2]; ActiveEnt const child = rSpawnerEnts[i * 2 + 1]; + DrawEnt const drawEnt = rDrawing.m_activeToDraw[child]; - rDrawing.m_mesh.emplace( child, rDrawing.m_meshRefCounts.ref_add(rNMesh.m_shapeToMesh.at(spawn.m_shape)) ); - rDrawing.m_meshDirty.push_back(child); + rDrawing.m_needDrawTf.set(std::size_t(root)); + rDrawing.m_needDrawTf.set(std::size_t(child)); - rMatEnts.set(std::size_t(child)); - rMatDirty.push_back(child); + rDrawing.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(rNMesh.m_shapeToMesh.at(spawn.m_shape)); + rDrawing.m_meshDirty.push_back(drawEnt); - rDrawing.m_visible.emplace(child); - rDrawing.m_opaque.emplace(child); + rMatEnts.set(std::size_t(drawEnt)); + rMatDirty.push_back(drawEnt); - rDrawing.m_drawable.set(std::size_t(root)); - rDrawing.m_drawable.set(std::size_t(child)); + rDrawing.m_visible.set(std::size_t(drawEnt)); + rDrawing.m_drawBasic[drawEnt].m_opaque = true; } })); @@ -320,10 +330,10 @@ Session setup_prefabs( prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabReq, tgPrefabEntReq, tgDrawMod, tgMeshMod, tgMatMod}).data( "Init Prefab drawables (single material)", - TopDataIds_t{ idPrefabInit, idResources, idDrawing, idDrawingRes, idMatEnts, idMatDirty, idActiveIds }, - wrap_args([] (ACtxPrefabInit& rPrefabInit, Resources& rResources, ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, EntSet_t& rMatEnts, EntVector_t& rMatDirty, ActiveReg_t const& rActiveIds) noexcept + TopDataIds_t{ idPrefabInit, idResources, idDrawing, idDrawingRes, idMatEnts, idMatDirty, idActiveIds }, + wrap_args([] (ACtxPrefabInit& rPrefabInit, Resources& rResources, ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, EntSet_t& rMatEnts, std::vector& rMatDirty, ActiveReg_t const& rActiveIds) noexcept { - rDrawing.m_drawable.ints().resize(rActiveIds.vec().capacity()); + rDrawing.resize_active(rActiveIds.capacity()); rMatEnts.ints().resize(rActiveIds.vec().capacity()); SysPrefabInit::init_drawing(rPrefabInit, rResources, rDrawing, rDrawingRes, {{rMatEnts, rMatDirty}}); })); diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index f4529cc2..768f02da 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -136,6 +136,16 @@ Session setup_scene_renderer( rBuilder.tag(tgDrawTransformMod) .depend_on({tgDrawTransformDel, tgDrawTransformNew}); rBuilder.tag(tgDrawTransformReq) .depend_on({tgDrawTransformDel, tgDrawTransformNew, tgDrawTransformMod}); + renderer.task() = rBuilder.task().assign({tgSyncEvt, tgTexGlMod, tgMeshGlMod, tgDrawGlMod, tgDrawReq}).data( + "Resize Scene Render containers to fit drawable entities", + TopDataIds_t{ idDrawing, idScnRender}, + wrap_args([] (ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept + { + std::size_t const capacity = rDrawing.m_drawIds.capacity(); + rScnRender.m_drawTransform .resize(capacity); + rScnRender.m_diffuseTexId .resize(capacity); + rScnRender.m_meshId .resize(capacity); + })); renderer.task() = rBuilder.task().assign({tgSyncEvt, tgGlUse, tgTexGlMod, tgTexGlMod}).data( "Synchronize used mesh and texture Resources with GL", @@ -189,7 +199,7 @@ Session setup_scene_renderer( SysRenderGL::display_texture(rRenderGl, rFboColor); })); - renderer.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderMod, tgDrawTransformReq, tgGroupFwdReq, tgDrawReq, tgCameraReq, tgEntTexMod, tgEntMeshMod}).data( + renderer.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderMod, tgDrawTransformReq, tgGroupFwdReq, tgDrawGlReq, tgCameraReq, tgEntTexMod, tgEntMeshMod}).data( "Render Entities", TopDataIds_t{ idDrawing, idRenderGl, idGroupFwd, idCamera}, wrap_args([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept @@ -214,15 +224,25 @@ Session setup_scene_renderer( wrap_args([] (ACtxBasic const& rBasic, ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept { auto rootChildren = SysSceneGraph::children(rBasic.m_scnGraph); - SysRender::update_draw_transforms(rBasic.m_scnGraph, rBasic.m_transform, rScnRender.m_drawTransform, rDrawing.m_drawable, std::begin(rootChildren), std::end(rootChildren)); + SysRender::update_draw_transforms( + rBasic.m_scnGraph, + rDrawing.m_activeToDraw, + rBasic.m_transform, + rScnRender.m_drawTransform, + rDrawing.m_needDrawTf, + std::begin(rootChildren), + std::end(rootChildren)); })); - renderer.task() = rBuilder.task().assign({tgSyncEvt, tgDelTotalReq, tgGroupFwdDel}).data( + renderer.task() = rBuilder.task().assign({tgSyncEvt, tgGroupFwdDel, tgDelDrawEntReq}).data( "Delete entities from render groups", - TopDataIds_t{ idGroupFwd, idDelTotal}, - wrap_args([] (RenderGroup& rGroup, EntVector_t const& rDelTotal) noexcept + TopDataIds_t{ idDrawing, idGroupFwd, idDelDrawEnts}, + wrap_args([] (ACtxDrawing const& rDrawing, RenderGroup& rGroup, DrawEntVector_t const& rDelDrawEnts) noexcept { - rGroup.m_entities.remove(std::cbegin(rDelTotal), std::cend(rDelTotal)); + for (DrawEnt const drawEnt : rDelDrawEnts) + { + rGroup.m_entities.remove(drawEnt); + } })); return renderer; @@ -265,8 +285,8 @@ Session setup_shader_visualizer( shVisual.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgGroupFwdMod}).data( "Sync MeshVisualizer shader entities", - TopDataIds_t{ idMatDirty, idMatEnts, idGroupFwd, idDrawShVisual}, - wrap_args([] (EntVector_t const& rMatDirty, EntSet_t const& rMatEnts, RenderGroup& rGroup, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept + TopDataIds_t{ idMatDirty, idMatEnts, idGroupFwd, idDrawShVisual}, + wrap_args([] (std::vector const& rMatDirty, EntSet_t const& rMatEnts, RenderGroup& rGroup, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept { sync_visualizer(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, rGroup.m_entities, rDrawShVisual); })); @@ -274,9 +294,9 @@ Session setup_shader_visualizer( shVisual.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgTransformReq, tgDrawTransformNew}).data( "Add draw transforms to mesh visualizer", TopDataIds_t{ idMatDirty, idScnRender}, - wrap_args([] (EntVector_t const& rMatDirty, ACtxSceneRenderGL& rScnRender) noexcept + wrap_args([] (std::vector const& rMatDirty, ACtxSceneRenderGL& rScnRender) noexcept { - SysRender::assure_draw_transforms(rScnRender.m_drawTransform, std::cbegin(rMatDirty), std::cend(rMatDirty)); + //SysRender::assure_draw_transforms(rScnRender.m_drawTransform, std::cbegin(rMatDirty), std::cend(rMatDirty)); })); @@ -320,18 +340,10 @@ Session setup_shader_flat( shFlat.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, tgTexGlReq, tgGroupFwdMod}).data( "Sync Flat shader entities", - TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShFlat}, - wrap_args([] (EntVector_t const& rMatDirty, EntSet_t const& rMatEnts, ACtxDrawing const& rDrawing, ACtxSceneRenderGL const& rScnRender, RenderGroup& rGroupFwd, ACtxDrawFlat& rDrawShFlat) noexcept - { - sync_flat(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, &rGroupFwd.m_entities, nullptr, rDrawing.m_opaque, rScnRender.m_diffuseTexId, rDrawShFlat); - })); - - shFlat.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgTransformReq, tgDrawTransformNew}).data( - "Add draw transforms to entities with Phong shader", - TopDataIds_t{ idMatDirty, idScnRender}, - wrap_args([] (EntVector_t const& rMatDirty, ACtxSceneRenderGL& rScnRender) noexcept + TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShFlat}, + wrap_args([] (std::vector const& rMatDirty, EntSet_t const& rMatEnts, ACtxDrawing const& rDrawing, ACtxSceneRenderGL const& rScnRender, RenderGroup& rGroupFwd, ACtxDrawFlat& rDrawShFlat) noexcept { - SysRender::assure_draw_transforms(rScnRender.m_drawTransform, std::cbegin(rMatDirty), std::cend(rMatDirty)); + sync_flat(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, &rGroupFwd.m_entities, nullptr, rDrawing.m_drawBasic, rScnRender.m_diffuseTexId, rDrawShFlat); })); return shFlat; @@ -376,18 +388,10 @@ Session setup_shader_phong( shPhong.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, tgTexGlReq, tgGroupFwdMod}).data( "Sync Phong shader entities", - TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShPhong}, - wrap_args([] (EntVector_t const& rMatDirty, EntSet_t const& rMatEnts, ACtxDrawing const& rDrawing, ACtxSceneRenderGL const& rScnRender, RenderGroup& rGroupFwd, ACtxDrawPhong& rDrawShPhong) noexcept + TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShPhong}, + wrap_args([] (std::vector const& rMatDirty, EntSet_t const& rMatEnts, ACtxDrawing const& rDrawing, ACtxSceneRenderGL const& rScnRender, RenderGroup& rGroupFwd, ACtxDrawPhong& rDrawShPhong) noexcept { - sync_phong(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, &rGroupFwd.m_entities, nullptr, rDrawing.m_opaque, rScnRender.m_diffuseTexId, rDrawShPhong); - })); - - shPhong.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgTransformReq, tgDrawTransformNew}).data( - "Add draw transforms to entities with Phong shader", - TopDataIds_t{ idMatDirty, idScnRender}, - wrap_args([] (EntVector_t const& rMatDirty, ACtxSceneRenderGL& rScnRender) noexcept - { - SysRender::assure_draw_transforms(rScnRender.m_drawTransform, std::cbegin(rMatDirty), std::cend(rMatDirty)); + sync_phong(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, &rGroupFwd.m_entities, nullptr, rDrawing.m_drawBasic, rScnRender.m_diffuseTexId, rDrawShPhong); })); return shPhong; @@ -414,6 +418,8 @@ Session setup_thrust_indicators( TopDataId const idResources, PkgId const pkg) { + + using namespace osp::link; using adera::gc_mtMagicRocket; using adera::ports_magicrocket::gc_throttleIn; @@ -443,6 +449,7 @@ Session setup_thrust_indicators( auto &rDrawingRes = top_get< ACtxDrawingRes > (topData, idDrawingRes); Session thrustIndicator; +#if 0 // RENDERENT OSP_SESSION_ACQUIRE_DATA(thrustIndicator, topData, TESTAPP_INDICATOR); thrustIndicator.m_tgCleanupEvt = tgCleanupMagnumEvt; @@ -543,7 +550,7 @@ Session setup_thrust_indicators( } } })); - +#endif return thrustIndicator; } From decd8b970cfff187093d6b1127616b18b8e31e2c Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 17 Apr 2023 00:55:49 -0700 Subject: [PATCH 10/35] Rewrite Tasks to use "Targets" instead of Tags --- src/osp/bitvector.h | 3 +- src/osp/tasks/builder.cpp | 132 ++++++++++++++++++++++ src/osp/tasks/builder.h | 228 +++++++++++++++----------------------- src/osp/tasks/tasks.h | 94 ++++------------ test/tasks/CMakeLists.txt | 3 +- test/tasks/main.cpp | 135 ++++++++++++++-------- 6 files changed, 337 insertions(+), 258 deletions(-) create mode 100644 src/osp/tasks/builder.cpp diff --git a/src/osp/bitvector.h b/src/osp/bitvector.h index 79855fb4..81fb33d2 100644 --- a/src/osp/bitvector.h +++ b/src/osp/bitvector.h @@ -32,7 +32,8 @@ namespace osp { -using BitVector_t = lgrn::BitView< std::vector >; +using bitint_t = uint64_t; +using BitVector_t = lgrn::BitView< std::vector >; inline void bitvector_resize(BitVector_t &rBitVector, std::size_t size) { diff --git a/src/osp/tasks/builder.cpp b/src/osp/tasks/builder.cpp new file mode 100644 index 00000000..dd432b43 --- /dev/null +++ b/src/osp/tasks/builder.cpp @@ -0,0 +1,132 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * 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 "builder.h" + +namespace osp +{ + +Tasks finalize(TaskBuilderData && data) +{ + using TargetCounts = TaskBuilderData::TargetCounts; + using TaskCounts = TaskBuilderData::TaskCounts; + using TargetInt = Tasks::TargetInt; + using TaskInt = Tasks::TaskInt; + + Tasks out; + + KeyedVec taskCounts; + KeyedVec targetCounts; + + taskCounts .resize(data.m_taskIds .capacity()); + targetCounts.resize(data.m_targetIds.capacity()); + + // Count connections + + for (auto const [task, target] : data.m_targetDependEdges) + { + ++ taskCounts[task] .m_dependingOn; + ++ targetCounts[target].m_dependents; + } + + for (auto const [task, target] : data.m_targetFulfillEdges) + { + ++ taskCounts[task] .m_fulfills; + ++ targetCounts[target].m_fulfilledBy; + } + + // Allocate + + out.m_taskDependOn .ids_reserve (data.m_taskIds.capacity()); + out.m_taskDependOn .data_reserve (data.m_targetDependEdges.size()); + out.m_targetDependents .ids_reserve (data.m_targetIds.capacity()); + out.m_targetDependents .data_reserve (data.m_targetDependEdges.size()); + out.m_taskFulfill .ids_reserve (data.m_taskIds.capacity()); + out.m_taskFulfill .data_reserve (data.m_targetFulfillEdges.size()); + out.m_targetFulfilledBy .ids_reserve (data.m_targetIds.capacity()); + out.m_targetFulfilledBy .data_reserve (data.m_targetFulfillEdges.size()); + + // Reserve partitions + + for (Tasks::TaskInt const task : data.m_taskIds.bitview().zeros()) + { + if (std::size_t const size = taskCounts[TaskId(task)].m_dependingOn; + size != 0) + { + out.m_taskDependOn.emplace(task, size); + } + if (std::size_t const size = taskCounts[TaskId(task)].m_fulfills; + size != 0) + { + out.m_taskFulfill.emplace(task, size); + } + } + + for (Tasks::TargetInt const target : data.m_targetIds.bitview().zeros()) + { + if (std::size_t const size = targetCounts[TargetId(target)].m_dependents; + size != 0) + { + out.m_targetDependents.emplace(target, size); + } + + if (std::size_t const size = targetCounts[TargetId(target)].m_fulfilledBy; + size != 0) + { + out.m_targetFulfilledBy.emplace(target, size); + } + } + + // Place + + for (auto const [task, target] : data.m_targetDependEdges) + { + --taskCounts[task] .m_dependingOn; + --targetCounts[target].m_dependents; + + out.m_taskDependOn [TaskInt(task)] [taskCounts[task].m_dependingOn] = target; + out.m_targetDependents [TargetInt(target)] [targetCounts[target].m_dependents] = task; + } + + for (auto const [task, target] : data.m_targetFulfillEdges) + { + -- taskCounts[task] .m_fulfills; + -- targetCounts[target].m_fulfilledBy; + + out.m_taskFulfill [TaskInt(task)] [taskCounts[task].m_fulfills] = target; + out.m_targetFulfilledBy [TargetInt(target)] [targetCounts[target].m_fulfilledBy] = task; + } + + out.m_taskIds = std::move(data.m_taskIds); + out.m_targetIds = std::move(data.m_targetIds); + out.m_semaIds = std::move(data.m_semaIds); + out.m_semaLimits = std::move(data.m_semaLimits); + + return out; +} + + + +} // namespace osp + diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 91d00de1..cf45f254 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -25,7 +25,9 @@ #pragma once #include "tasks.h" +#include "../types.h" +#include #include #include @@ -38,178 +40,130 @@ namespace osp { -using Corrade::Containers::ArrayView; - -inline void set_tags( - Corrade::Containers::ArrayView tagsIn, - ArrayView taggedInts, - std::size_t const taggedSize, - std::size_t const taggedId) noexcept +struct TaskBuilderData { - std::size_t const offset = taggedId * taggedSize; - auto taggedBits = lgrn::BitView(taggedInts.slice(offset, offset + taggedSize)); + struct TaskTargetPair + { + TaskId m_task; + TargetId m_target; + }; - for (uint32_t const tag : tagsIn) + struct TaskSemaphorePair { - taggedBits.set(tag); - } -} + TaskId m_task; + SemaphoreId m_sema; + }; + + struct TaskCounts + { + unsigned int m_dependingOn {0}; + unsigned int m_fulfills {0}; + unsigned int m_acquires {0}; + }; + + struct TargetCounts + { + unsigned int m_dependents {0}; + unsigned int m_fulfilledBy {0}; + }; + + lgrn::IdRegistryStl m_taskIds; + lgrn::IdRegistryStl m_targetIds; + lgrn::IdRegistryStl m_semaIds; + KeyedVec m_semaLimits; + + std::vector m_targetDependEdges; + std::vector m_targetFulfillEdges; + std::vector m_semaphoreEdges; + +}; // TaskBuilderData /** - * @brief A convenient interface for setting up TaskTags and required task data + * @brief A convenient interface for setting up Tasks and required task data */ -template -class TaskBuilder +template +struct TaskBuilderBase : TaskBuilderData { - using Tags_t = Corrade::Containers::ArrayView; - public: - struct TaskRef + TASKREF_T task() { - constexpr operator TaskId() noexcept - { - return m_taskId; - } - - TaskRef& assign(Tags_t const tags) noexcept - { - set_tags(Corrade::Containers::arrayCast(tags), - m_rTasks.m_taskTags, - m_rTags.tag_ints_per_task(), - std::size_t(m_taskId)); - return *this; - } - - TaskRef& assign(std::initializer_list tags) noexcept - { - return assign(Corrade::Containers::arrayView(tags)); - } - - template - TaskRef& data(ARGS_T&& ... args) noexcept - { - task_data(m_rData, m_taskId, std::forward(args) ...); - return *this; - } + TaskId const taskId = m_taskIds.create(); - TaskId const m_taskId; - Tags & m_rTags; - Tasks & m_rTasks; - DATA_T & m_rData; + std::size_t const capacity = m_taskIds.capacity(); - }; // struct TaskRefSpec + return task(taskId); + }; - struct TagRef + constexpr TASKREF_T task(TaskId const taskId) noexcept { + return TASKREF_T{ + taskId, + static_cast(*this) + }; + } - TagRef& depend_on(Tags_t tags) noexcept - { - auto depends = osp::tag_depends_2d(m_rTags)[std::size_t(m_tagId)].asContiguous(); - auto const depBegin = std::begin(depends); - auto const depEnd = std::end(depends); - - for (TagId const tag : tags) - { - // Find first empty spot or already-added dependency - auto dependIt = std::find_if( - depBegin, depEnd, [tag] (TagId const lhs) - { - return (lhs == lgrn::id_null()) || (lhs == tag); - }); - - // No space left for any dependencies - assert(dependIt != depEnd); - - *dependIt = tag; - } - - return *this; - } + template + TGT_STRUCT_T create_targets() + { + static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); - TagRef& depend_on(std::initializer_list tags) noexcept - { - return depend_on(Corrade::Containers::arrayView(tags)); - } + std::array out; - TagRef& limit(unsigned int lim) - { - m_rTags.m_tagLimits[std::size_t(m_tagId)] = lim; - return *this; - } + m_targetIds.create(out.begin(), out.end()); - TagRef& set_external(bool value) - { - auto bitView = lgrn::bit_view(m_rTags.m_tagExtern); - if (value) - { - bitView.set(std::size_t(m_tagId)); - } - else - { - bitView.reset(std::size_t(m_tagId)); - } - return *this; - } + return reinterpret_cast(*out.data()); + } - TagId const m_tagId; - Tags & m_rTags; +}; // class TaskBuilderBase - }; // struct TagRef - constexpr TaskBuilder(Tags& rTags, Tasks& rTasks, DATA_T& rData) - : m_rTags{rTags} - , m_rTasks{rTasks} - , m_rData{rData} - { } +template +struct TaskRefBase +{ + struct Empty {}; - template - std::array create_tags() + constexpr operator TaskId() noexcept { - std::array out; - - [[maybe_unused]] auto const it - = m_rTags.m_tags.create(std::begin(out), std::end(out)); + return m_taskId; + } - assert(it == std::end(out)); // Auto-resizing tags not (yet?) supported + constexpr Tasks & tasks() noexcept { return m_rBuilder.m_rTasks; } - return out; + TASKREF_T& depends_on(ArrayView const targets) noexcept + { + for (TargetId const target : targets) + { + m_rBuilder.m_targetDependEdges.push_back({m_taskId, target}); + } + return static_cast(*this); } - TagRef tag(TagId const tagId) + TASKREF_T& depends_on(std::initializer_list targets) noexcept { - return { - .m_tagId = tagId, - .m_rTags = m_rTags - }; + return depends_on(Corrade::Containers::arrayView(targets)); } - TaskRef task() + TASKREF_T& fulfills(ArrayView const targets) noexcept { - TaskId const taskId = m_rTasks.m_tasks.create(); - - std::size_t const capacity = m_rTasks.m_tasks.capacity(); - - m_rTasks.m_taskTags.resize(capacity * m_rTags.tag_ints_per_task()); - - return task(taskId); - }; + for (TargetId const target : targets) + { + m_rBuilder.m_targetFulfillEdges.push_back({m_taskId, target}); + } + return static_cast(*this); + } - constexpr TaskRef task(TaskId const taskId) noexcept + TASKREF_T& fulfills(std::initializer_list targets) noexcept { - return { - .m_taskId = taskId, - .m_rTags = m_rTags, - .m_rTasks = m_rTasks, - .m_rData = m_rData - }; + return fulfills(Corrade::Containers::arrayView(targets)); } -private: - Tags & m_rTags; - Tasks & m_rTasks; - DATA_T & m_rData; + TaskId m_taskId; + TASKBUILDER_T & m_rBuilder; + +}; // struct TaskRefBase -}; // class TaskBuilder +Tasks finalize(TaskBuilderData && data); } // namespace osp diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 9f0a69b2..941c3775 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -24,7 +24,10 @@ */ #pragma once +#include "../keyed_vector.h" + #include +#include #include @@ -34,91 +37,34 @@ namespace osp { -using bit_int_t = uint64_t; - -enum class TaskId : uint32_t {}; -enum class TagId : uint32_t {}; - -struct Tags -{ - lgrn::IdRegistryStl m_tags; - - // Dependency tags restricts a task from running until all tasks containing - // tags it depends on are complete. - // 2D with m_tagDependsPerTag elements per row, [tag][dependency] - // lgrn::null used as null, or termination per-tag - std::vector m_tagDepends; - std::size_t m_tagDependsPerTag{8}; - - // Limit sets how many tasks using a certain tag can run simultaneously - std::vector m_tagLimits; - - // Restricts associated enqueued tasks from running until externally set - // Resets once all associated tasks are complete - std::vector m_tagExtern; - - [[nodiscard]] std::size_t tag_ints_per_task() const noexcept { return m_tags.vec().size(); } - -}; // struct Tags +enum class TaskId : uint32_t { }; +enum class TargetId : uint32_t { }; +enum class SemaphoreId : uint32_t { }; struct Tasks { - lgrn::IdRegistryStl m_tasks; + using TaskInt = uint32_t; + using TargetInt = uint32_t; + using SemaphoreInt = uint32_t; - // Bit positions are used to store which tags a task contains. - // 2D with Tags::m_tags.vec().size() elements per row. [task][tagsInt] - // partitioned based on Tags::tag_ints_per_task(): AAAABBBBCCCCDDDD - std::vector m_taskTags; + lgrn::IdRegistryStl m_taskIds; + lgrn::IdRegistryStl m_targetIds; + lgrn::IdRegistryStl m_semaIds; -}; // struct Tasks + KeyedVec m_semaLimits; -inline Corrade::Containers::StridedArrayView2D tag_depends_2d(Tags& rTags) noexcept -{ - return {Corrade::Containers::arrayView(rTags.m_tagDepends.data(), rTags.m_tagDepends.size()), - {rTags.m_tags.capacity(), rTags.m_tagDependsPerTag}}; -} + lgrn::IntArrayMultiMap m_taskDependOn; /// Tasks depend on (n) Targets + lgrn::IntArrayMultiMap m_targetDependents; /// Targets have (n) Tasks that depend on it -inline Corrade::Containers::StridedArrayView2D tag_depends_2d(Tags const& rTags) noexcept -{ - return {Corrade::Containers::arrayView(rTags.m_tagDepends.data(), rTags.m_tagDepends.size()), - {rTags.m_tags.capacity(), rTags.m_tagDependsPerTag}}; -} - -inline Corrade::Containers::StridedArrayView2D task_tags_2d(Tags const& rTags, Tasks &rTasks) noexcept -{ - return {Corrade::Containers::arrayView(rTasks.m_taskTags.data(), rTasks.m_taskTags.size()), - {rTasks.m_tasks.capacity(), rTags.tag_ints_per_task()}}; -} - -inline Corrade::Containers::StridedArrayView2D task_tags_2d(Tags const& rTags, Tasks const &rTasks) noexcept -{ - return {Corrade::Containers::arrayView(rTasks.m_taskTags.data(), rTasks.m_taskTags.size()), - {rTasks.m_tasks.capacity(), rTags.tag_ints_per_task()}}; -} + lgrn::IntArrayMultiMap m_taskFulfill; /// Tasks fulfill (n) Targets + lgrn::IntArrayMultiMap m_targetFulfilledBy; /// Targets are fulfilled by (n) Tasks -template -struct TaskDataVec -{ - std::vector m_taskData; - -}; // struct TaskDataVec - -template -void task_data(TaskDataVec &rData, TaskId const task, RHS_T&& rhs) -{ - rData.m_taskData.resize( - std::max(rData.m_taskData.size(), std::size_t(task) + 1)); - rData.m_taskData[std::size_t(task)] = std::forward(rhs); -} + lgrn::IntArrayMultiMap m_taskAcquire; /// Tasks acquire (n) Semaphores + lgrn::IntArrayMultiMap m_semaAcquiredBy; /// Semaphores are acquired by (n) Tasks +}; struct ExecutionContext { - // Tag counts from running or queued tasks; used for Dependencies - std::vector m_tagIncompleteCounts; - // Tag counts from running tasks; used for Limits - std::vector m_tagRunningCounts; - // External tags - std::vector m_tagExternTriggers; // Number of times each task is queued to run std::vector m_taskQueuedCounts; diff --git a/test/tasks/CMakeLists.txt b/test/tasks/CMakeLists.txt index a7921cc9..6ebcb6ec 100644 --- a/test/tasks/CMakeLists.txt +++ b/test/tasks/CMakeLists.txt @@ -26,5 +26,6 @@ PROJECT(test_tasks CXX) ADD_TEST_DIRECTORY(${PROJECT_NAME}) TARGET_LINK_LIBRARIES(test_tasks PRIVATE longeron EnTT::EnTT Magnum::Magnum) -TARGET_SOURCES(test_tasks PRIVATE "${CMAKE_SOURCE_DIR}/src/osp/tasks/execute_simple.cpp") +TARGET_SOURCES(test_tasks PRIVATE "${CMAKE_SOURCE_DIR}/src/osp/tasks/builder.cpp") +#TARGET_SOURCES(test_tasks PRIVATE "${CMAKE_SOURCE_DIR}/src/osp/tasks/execute_simple.cpp") diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index e1c68a71..10e592ef 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -24,7 +24,7 @@ */ #include #include -#include +//#include #include @@ -34,6 +34,19 @@ using namespace osp; +template +bool contains(RANGE_T const& range, VALUE_T const& value) +{ + for (auto const& element : range) + { + if (element == value) + { + return true; + } + } + return false; +} + struct World { int m_deltaTimeIn{0}; @@ -42,6 +55,36 @@ struct World std::set m_canvas; }; +using TaskFunctions_t = KeyedVec>; + +struct CustomTaskBuilder; + +struct CustomTaskRef : public osp::TaskRefBase +{ + CustomTaskRef& func(std::function in); +}; + +struct CustomTaskBuilder : public osp::TaskBuilderBase +{ + TaskFunctions_t & m_funcs; +}; + +CustomTaskRef& CustomTaskRef::func(std::function in) +{ + m_rBuilder.m_funcs.resize(m_rBuilder.m_taskIds.capacity()); + m_rBuilder.m_funcs[m_taskId] = std::move(in); + return *this; +} + +struct WorldTargets +{ + TargetId timeIn; /// Fire externally when time changes, and world needs to update + TargetId forces; /// Forces need to be calculated for physics + TargetId physics; /// Physics calculations + TargetId renderRequestIn; /// Fire externally when a new frame to render is required + TargetId renderDoneOut; /// Fired when all rendering is finished +}; + // Single-threaded test against World with order-dependent tasks TEST(Tasks, SingleThreaded) { @@ -50,72 +93,52 @@ TEST(Tasks, SingleThreaded) // Setup structs for storing tasks and tags - Tags tags; - Tasks tasks; - tags.m_tags.reserve(128); // Max 128 tags, aka: just two 64-bit integers - tags.m_tagDepends.resize(tags.m_tags.capacity() * tags.m_tagDependsPerTag, - lgrn::id_null()); - tags.m_tagLimits.resize(tags.m_tags.capacity()); - tags.m_tagExtern.resize(tags.m_tags.capacity()); - - TaskDataVec> functions; + KeyedVec> functions; // Create tags and set relationships between them - auto builder = TaskBuilder{tags, tasks, functions}; + auto builder = CustomTaskBuilder{.m_funcs = functions}; - // Tags are simply enum class integers - auto const [updWorld, updRender, forces, physics, needPhysics, draw] - = builder.create_tags<6>(); + auto const tgt = builder.create_targets(); - // Limit sets how many tasks using a certain tag can run simultaneously, but - // are unused in this test. - // Dependency tags restricts a task from running until all tasks containing - // tags it depends on are compelete. - builder.tag(forces).limit(1); - builder.tag(physics).depend_on({forces}); - builder.tag(needPhysics).depend_on({physics}); - builder.tag(draw).limit(1); // Start adding tasks / systems. The order these are added does not matter. + // Calculate forces needed by the physics update - builder.task() - .assign({updWorld, forces}) - .data([] (World& rWorld) + TaskId const taskA = builder.task() + .depends_on({tgt.timeIn}) + .fulfills({tgt.forces}) + .func( [] (World& rWorld) { rWorld.m_forces += 42 * rWorld.m_deltaTimeIn; }); - builder.task() - .assign({updWorld, forces}) - .data([] (World& rWorld) + + TaskId const taskB = builder.task() + .depends_on({tgt.timeIn}) + .fulfills({tgt.forces}) + .func([] (World& rWorld) { rWorld.m_forces += 1337 * rWorld.m_deltaTimeIn; }); // Main Physics update - builder.task() - .assign({updWorld, physics}) - .data([] (World& rWorld) + TaskId const taskC = builder.task() + .depends_on({tgt.timeIn, tgt.forces}) + .fulfills({tgt.physics}) + .func([] (World& rWorld) { EXPECT_EQ(rWorld.m_forces, 1337 + 42); rWorld.m_positions += rWorld.m_forces; rWorld.m_forces = 0; }); - // Physics update can be split into many smaller tasks. Tasks tagged with - // 'needPhysics' will run once ALL tasks tagged with 'physics' are done. - builder.task() - .assign({updWorld, physics}) - .data([] (World& rWorld) - { - rWorld.m_deltaTimeIn = 0; - }); // Draw things moved by physics update. If 'updWorld' wasn't enqueued, then // this will still run, as no 'needPhysics' tasks are incomplete - builder.task() - .assign({updRender, needPhysics, draw}) - .data([] (World& rWorld) + TaskId const taskD = builder.task() + .depends_on({tgt.physics, tgt.renderRequestIn}) + .fulfills({tgt.renderDoneOut}) + .func([] (World& rWorld) { EXPECT_EQ(rWorld.m_positions, 1337 + 42); rWorld.m_canvas.emplace("Physics Cube"); @@ -123,13 +146,34 @@ TEST(Tasks, SingleThreaded) // Draw things unrelated to physics. This is allowed to be the first task // to run - builder.task() - .assign({updRender, draw}) - .data([] (World& rWorld) + TaskId const taskE = builder.task() + .depends_on({tgt.renderRequestIn}) + .fulfills({tgt.renderDoneOut}) + .func([] (World& rWorld) { rWorld.m_canvas.emplace("Terrain"); }); + Tasks const tasks = finalize(std::move(builder)); + + // Some random checks to assure the graph structure is properly built + EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.timeIn) ].size(), 3); + EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.forces) ].size(), 1); + EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.physics) ].size(), 1); + EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.renderRequestIn) ].size(), 2); + EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.renderDoneOut) ].size(), 0); + EXPECT_TRUE( contains(tasks.m_targetFulfilledBy[uint32_t(tgt.forces)], taskB) ); + EXPECT_FALSE( contains(tasks.m_targetFulfilledBy[uint32_t(tgt.renderDoneOut)], taskC) ); + EXPECT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskA)], tgt.timeIn) ); + EXPECT_FALSE( contains(tasks.m_taskDependOn[uint32_t(taskB)], tgt.physics) ); + EXPECT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskC)], tgt.timeIn) ); + EXPECT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskC)], tgt.forces) ); + EXPECT_TRUE( contains(tasks.m_taskFulfill[uint32_t(taskD)], tgt.renderDoneOut) ); + EXPECT_FALSE( contains(tasks.m_taskFulfill[uint32_t(taskE)], tgt.forces) ); + + +#if 0 + // Start execution ExecutionContext exec; @@ -182,6 +226,7 @@ TEST(Tasks, SingleThreaded) ASSERT_TRUE(world.m_canvas.contains("Physics Cube")); ASSERT_TRUE(world.m_canvas.contains("Terrain")); } +#endif } From d80c440219b7b95672a15eab7174cdcd60a76904 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 23 Apr 2023 18:49:37 -0700 Subject: [PATCH 11/35] Progress on new Task system --- src/osp/keyed_bitview.h | 33 ---- src/osp/tasks/builder.cpp | 20 ++- src/osp/tasks/builder.h | 46 ++++- src/osp/tasks/execute.cpp | 128 ++++++++++++++ src/osp/tasks/execute.h | 81 +++++++++ src/osp/tasks/execute_simple.cpp | 223 ------------------------ src/osp/tasks/execute_simple.h | 124 -------------- src/osp/tasks/tasks.h | 22 +-- src/osp/tasks/top_execute.cpp | 2 +- src/osp/tasks/top_execute.h | 8 +- src/osp/tasks/top_session.cpp | 2 +- src/test_application/main.cpp | 2 +- test/tasks/CMakeLists.txt | 4 +- test/tasks/main.cpp | 280 +++++++++++++++++++------------ 14 files changed, 447 insertions(+), 528 deletions(-) delete mode 100644 src/osp/keyed_bitview.h create mode 100644 src/osp/tasks/execute.cpp create mode 100644 src/osp/tasks/execute.h delete mode 100644 src/osp/tasks/execute_simple.cpp delete mode 100644 src/osp/tasks/execute_simple.h diff --git a/src/osp/keyed_bitview.h b/src/osp/keyed_bitview.h deleted file mode 100644 index 54d18d02..00000000 --- a/src/osp/keyed_bitview.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Open Space Program - * Copyright © 2019-2023 Open Space Program Project - * - * MIT License - * - * 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 - -namespace osp -{ - - - -} // namespace osp diff --git a/src/osp/tasks/builder.cpp b/src/osp/tasks/builder.cpp index dd432b43..068d9dc6 100644 --- a/src/osp/tasks/builder.cpp +++ b/src/osp/tasks/builder.cpp @@ -24,7 +24,7 @@ */ #include "builder.h" -namespace osp +namespace osp::tasks { Tasks finalize(TaskBuilderData && data) @@ -102,20 +102,26 @@ Tasks finalize(TaskBuilderData && data) for (auto const [task, target] : data.m_targetDependEdges) { + auto const dependOn = lgrn::Span (out.m_taskDependOn[TaskInt(task)]); + auto const dependents = lgrn::Span (out.m_targetDependents[TargetInt(target)]); + + dependOn [dependOn.size() - taskCounts[task].m_dependingOn] = target; + dependents [dependents.size() - targetCounts[target].m_dependents] = task; + --taskCounts[task] .m_dependingOn; --targetCounts[target].m_dependents; - - out.m_taskDependOn [TaskInt(task)] [taskCounts[task].m_dependingOn] = target; - out.m_targetDependents [TargetInt(target)] [targetCounts[target].m_dependents] = task; } for (auto const [task, target] : data.m_targetFulfillEdges) { + auto const fulfills = lgrn::Span (out.m_taskFulfill[TaskInt(task)]); + auto const fulfilledBy = lgrn::Span (out.m_targetFulfilledBy[TargetInt(target)]); + + fulfills [fulfills.size() - taskCounts[task].m_fulfills] = target; + fulfilledBy [fulfilledBy.size() - targetCounts[target].m_fulfilledBy] = task; + -- taskCounts[task] .m_fulfills; -- targetCounts[target].m_fulfilledBy; - - out.m_taskFulfill [TaskInt(task)] [taskCounts[task].m_fulfills] = target; - out.m_targetFulfilledBy [TargetInt(target)] [targetCounts[target].m_fulfilledBy] = task; } out.m_taskIds = std::move(data.m_taskIds); diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index cf45f254..936ff96d 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -37,7 +37,7 @@ #include #include -namespace osp +namespace osp::tasks { struct TaskBuilderData @@ -116,6 +116,14 @@ struct TaskBuilderBase : TaskBuilderData return reinterpret_cast(*out.data()); } + template + std::array create_targets() + { + std::array out; + m_targetIds.create(out.begin(), out.end()); + return out; + } + }; // class TaskBuilderBase @@ -164,6 +172,42 @@ struct TaskRefBase }; // struct TaskRefBase +template +struct BasicBuilderTraits +{ + using Func_t = FUNC_T; + using FuncVec_t = KeyedVec; + + struct Builder; + + struct Ref : public TaskRefBase + { + Ref& func(FUNC_T && in); + }; + + struct Builder : public TaskBuilderBase + { + Builder(FuncVec_t& funcs) + : m_funcs{funcs} + { } + Builder(Builder const& copy) = delete; + Builder(Builder && move) = default; + + Builder& operator=(Builder const& copy) = delete; + Builder& operator=(Builder && move) = default; + + FuncVec_t & m_funcs; + }; +}; + +template +typename BasicBuilderTraits::Ref& BasicBuilderTraits::Ref::func(FUNC_T && in) +{ + this->m_rBuilder.m_funcs.resize(this->m_rBuilder.m_taskIds.capacity()); + this->m_rBuilder.m_funcs[this->m_taskId] = std::move(in); + return *this; +} + Tasks finalize(TaskBuilderData && data); } // namespace osp diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp new file mode 100644 index 00000000..53e498c7 --- /dev/null +++ b/src/osp/tasks/execute.cpp @@ -0,0 +1,128 @@ +/** + * Open Space Program + * Copyright © 2019-2022 Open Space Program Project + * + * MIT License + * + * 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 "execute.h" + + +namespace osp::tasks +{ + +bool try_enqueue_task(Tasks const& tasks, ExecutionContext &rExec, TaskId const task) noexcept +{ + if (rExec.m_tasksQueued.test(std::size_t(task))) + { + return false; // task already queued + } + + for (TargetId const dependOn : tasks.m_taskDependOn[std::size_t(task)]) + { + if ( rExec.m_targetPendingCount[dependOn] != 0 + || rExec.m_targetWillBePending.test(std::size_t(dependOn)) ) + { + return false; // Dependency target is or will be pending, wait for other tasks to finish + } + } + + for (TargetId const dependOn : tasks.m_taskDependOn[std::size_t(task)]) + { + ++ rExec.m_targetInUseCount[dependOn]; + } + + for (TargetId const fulfill : tasks.m_taskFulfill[std::size_t(task)]) + { + ++ rExec.m_targetPendingCount[fulfill]; + } + + rExec.m_tasksQueued.set(std::size_t(task)); + + return true; +} + +int enqueue_dirty(Tasks const& tasks, ExecutionContext &rExec) noexcept +{ + // Find out which targets will be pending + for (std::size_t const target : rExec.m_targetDirty.ones()) + { + for (TaskId const dependent : tasks.m_targetDependents[target]) + { + for (TargetId const fulfill : tasks.m_taskFulfill[std::size_t(dependent)]) + { + rExec.m_targetWillBePending.set(std::size_t(fulfill)); + } + } + } + + int tasksEnqueued = 0; + + for (std::size_t const target : rExec.m_targetDirty.ones()) + { + for (TaskId const dependent : tasks.m_targetDependents[target]) + { + tasksEnqueued += int(try_enqueue_task(tasks, rExec, dependent)); + } + } + rExec.m_targetWillBePending.reset(); + rExec.m_targetDirty.reset(); + + return tasksEnqueued; +} + +void mark_completed_task(Tasks const& tasks, ExecutionContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept +{ + LGRN_ASSERTMV(rExec.m_tasksQueued.test(std::size_t(task)), + "Task must be queued to have been allowed to run", std::size_t(task)); + + for (TargetId const dependOn : tasks.m_taskDependOn[std::size_t(task)]) + { + -- rExec.m_targetInUseCount[dependOn]; + + if (rExec.m_targetInUseCount[dependOn] == 0) + { + rExec.m_targetDirty.reset(std::size_t(dependOn)); + } + } + + auto const taskFulfill = lgrn::Span(tasks.m_taskFulfill[std::size_t(task)]); + + for (int i = 0; i < taskFulfill.size(); ++i) + { + TargetId const fulfill = taskFulfill[i]; + + -- rExec.m_targetPendingCount[fulfill]; + + if (rExec.m_targetPendingCount[fulfill] == 0) + { + if (dirty.test(i)) + { + rExec.m_targetDirty.set(std::size_t(fulfill)); + } + } + } + + rExec.m_tasksQueued.reset(std::size_t(task)); +} + + +} // namespace osp::tasks diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h new file mode 100644 index 00000000..032a1276 --- /dev/null +++ b/src/osp/tasks/execute.h @@ -0,0 +1,81 @@ +/** + * Open Space Program + * Copyright © 2019-2022 Open Space Program Project + * + * MIT License + * + * 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 "tasks.h" + +#include +#include + +#include +#include + +namespace osp::tasks +{ + +/** + * @brief Bitset returned by tasks to determine which fulfill targets should be marked dirty + */ +using FulfillDirty_t = lgrn::BitView>; + +struct ExecutionContext +{ + void resize(Tasks const& tasks) + { + std::size_t const maxTargets = tasks.m_targetIds.capacity(); + std::size_t const maxTasks = tasks.m_taskIds.capacity(); + + bitvector_resize(m_tasksQueued, maxTasks); + bitvector_resize(m_targetDirty, maxTargets); + bitvector_resize(m_targetWillBePending, maxTargets); + m_targetPendingCount.resize(maxTargets); + m_targetInUseCount .resize(maxTargets); + } + + BitVector_t m_tasksQueued; + BitVector_t m_targetDirty; + BitVector_t m_targetWillBePending; + KeyedVec m_targetPendingCount; + KeyedVec m_targetInUseCount; + + // TODO: Consider multithreading. something something work stealing... + // * Allow multiple threads to search for and execute tasks. Atomic access + // for ExecutionContext? Might be messy to implement. + // * Only allow one thread to search for tasks, assign tasks to other + // threads if they're available before running own task. Another thread + // can take over once it completes its task. May be faster as only one + // thread is modifying ExecutionContext, and easier to implement. + // * Plug into an existing work queue library? + +}; // struct ExecutionContext + + +int enqueue_dirty(Tasks const& tasks, ExecutionContext &rExec) noexcept; + +void mark_completed_task(Tasks const& tasks, ExecutionContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept; + + + +} // namespace osp::tasks diff --git a/src/osp/tasks/execute_simple.cpp b/src/osp/tasks/execute_simple.cpp deleted file mode 100644 index 80913acb..00000000 --- a/src/osp/tasks/execute_simple.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Open Space Program - * Copyright © 2019-2022 Open Space Program Project - * - * MIT License - * - * 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 "execute_simple.h" - -#include -#include - -using Corrade::Containers::ArrayView; -using Corrade::Containers::StridedArrayView2D; -using Corrade::Containers::arrayView; - -namespace osp -{ - -bool any_bits_match(BitSpanConst_t const lhs, BitSpanConst_t const rhs) -{ - auto itRhsInt = std::begin(rhs); - for (bit_int_t const lhsInt : lhs) - { - if ((lhsInt & (*itRhsInt)) != 0) - { - return true; - } - std::advance(itRhsInt, 1); - } - - return false; -} - -void task_enqueue(Tags const& tags, Tasks const& tasks, ExecutionContext &rExec, BitSpanConst_t const query) -{ - assert(query.size() == tags.tag_ints_per_task()); - - rExec.m_tagIncompleteCounts .resize(tags.m_tags.capacity(), 0); - rExec.m_tagRunningCounts .resize(tags.m_tags.capacity(), 0); - rExec.m_taskQueuedCounts .resize(tasks.m_tasks.capacity(), 0); - - task_for_each(tags, tasks, [&rExec, &query] - (uint32_t const currTask, ArrayView const currTags) - { - unsigned int &rQueuedCount = rExec.m_taskQueuedCounts[currTask]; - - if ( (rQueuedCount != 0) || ( ! any_bits_match(query, currTags) ) ) - { - return; // Ignore already-queued tasks, or if tags do not match query - } - - rQueuedCount = 1; // All good, now queue the task - - // Add the task's tags to the incomplete counts - auto const view = lgrn::bit_view(currTags); - for (uint32_t const tag : view.ones()) - { - rExec.m_tagIncompleteCounts[tag] ++; - } - }); -} - -void task_extern_set(ExecutionContext &rExec, TagId const tag, bool value) -{ - auto bitView = lgrn::bit_view(rExec.m_tagExternTriggers); - if (value) - { - bitView.set(std::size_t(tag)); - } - else - { - bitView.reset(std::size_t(tag)); - } -} - - -static bool tags_present(BitSpanConst_t const mask, BitSpanConst_t const taskTags) noexcept -{ - auto taskTagIntIt = std::begin(taskTags); - for (uint64_t const maskInt : mask) - { - uint64_t const taskTagInt = *taskTagIntIt; - - // No match if a 1 bit in taskTagInt corresponds with a 0 in maskInt - if ((maskInt & taskTagInt) != taskTagInt) - { - return false; - } - std::advance(taskTagIntIt, 1); - } - return true; -} - -void task_list_available(Tags const& tags, Tasks const& tasks, ExecutionContext const& exec, BitSpan_t tasksOut) -{ - assert(tasksOut.size() == tasks.m_tasks.vec().size()); - - // Bitmask makes it easy to compare the tags of a task - // 1 = allowed (default), 0 = not allowed - // All tags of a task must be allowed for the entire task to run. - // aka: All ones in a task's bits must corrolate to a one in the mask - std::size_t const maskSize = tags.m_tags.vec().size(); - std::vector mask(maskSize, ~uint64_t(0)); - auto maskBits = lgrn::bit_view(mask); - - auto tagDepends2d = tag_depends_2d(tags); - - // Check dependencies of each tag - // Set them 0 (disallow) in the mask if the tag has incomplete dependencies - for (uint32_t const currTag : tags.m_tags.bitview().zeros()) - { - bool satisfied = true; - auto const currTagDepends = tagDepends2d[currTag].asContiguous(); - - for (TagId const dependTag : currTagDepends) - { - if (dependTag == lgrn::id_null()) - { - break; - } - - if (exec.m_tagIncompleteCounts[std::size_t(dependTag)] != 0) - { - satisfied = false; - break; - } - } - - if ( ! satisfied) - { - maskBits.reset(currTag); - } - } - - // Check external dependencies - std::size_t const externSize = std::min({tags.m_tagExtern.size(), exec.m_tagExternTriggers.size(), maskSize}); - for (std::size_t i = 0; i < externSize; ++i) - { - mask[i] &= ~(tags.m_tagExtern[i] & exec.m_tagExternTriggers[i]); - } - - // TODO: Check Limits with exec.m_tagRunningCounts - - auto tasksOutBits = lgrn::bit_view(tasksOut); - - std::size_t const tagIntSize = tags.tag_ints_per_task(); - BitSpanConst_t const taskTagInts = Corrade::Containers::arrayView(tasks.m_taskTags); - - // Iterate all tasks and use mask to match which ones can run - for (uint32_t const currTask : tasks.m_tasks.bitview().zeros()) - { - if (exec.m_taskQueuedCounts[currTask] == 0) - { - continue; // Task not queued to run - } - - std::size_t const offset = currTask * tagIntSize; - auto const currTaskTagInts = taskTagInts.slice(offset, offset + tagIntSize); - - if (tags_present(mask, currTaskTagInts)) - { - tasksOutBits.set(currTask); - } - } -} - -void task_start(Tags const& tags, Tasks const& tasks, ExecutionContext &rExec, TaskId const task) -{ - auto currTaskTagInts = task_tags_2d(tags, tasks)[std::size_t(task)].asContiguous(); - - auto const view = lgrn::bit_view(currTaskTagInts); - for (uint32_t const tag : view.ones()) - { - rExec.m_tagRunningCounts[tag] ++; - } - - rExec.m_taskQueuedCounts[std::size_t(task)] --; -} - -void task_finish(Tags const& tags, Tasks const& tasks, ExecutionContext &rExec, TaskId const task) -{ - auto const externBits = lgrn::bit_view(tags.m_tagExtern); - - auto taskTagInts = task_tags_2d(tags, tasks)[std::size_t(task)].asContiguous(); - auto const taskTags = lgrn::bit_view(taskTagInts); - - for (uint32_t const tag : taskTags.ones()) - { - rExec.m_tagRunningCounts[tag] --; - rExec.m_tagIncompleteCounts[tag] --; - - // If last task to finish with this tag - if (rExec.m_tagIncompleteCounts[tag] == 0) - { - // Reset external dependency bit - if (externBits.test(tag)) - { - task_extern_set(rExec, TagId(tag), false); - } - } - } -} - -} // namespace osp diff --git a/src/osp/tasks/execute_simple.h b/src/osp/tasks/execute_simple.h deleted file mode 100644 index 62309f52..00000000 --- a/src/osp/tasks/execute_simple.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Open Space Program - * Copyright © 2019-2022 Open Space Program Project - * - * MIT License - * - * 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 "tasks.h" - -#include -#include - -#include -#include - -namespace osp -{ - -using BitSpan_t = Corrade::Containers::ArrayView; -using BitSpanConst_t = Corrade::Containers::ArrayView; - -/** - * @brief Convert a range of ints or enums to bit positions - * - * {0, 1, 7, 4} -> 0x1010011 - * - * @param range [in] Range of ints or enum classes - * @param bitsOut [out] Span of ints large enough to fit all bits in range - * - * @return bitsOut - */ -template -BitSpan_t to_bitspan(RANGE_T const& range, BitSpan_t bitsOut) noexcept -{ - auto outBits = lgrn::bit_view(bitsOut); - outBits.reset(); - for (auto const pos : range) - { - outBits.set(std::size_t(pos)); - } - return bitsOut; -} - -template -BitSpan_t to_bitspan(std::initializer_list tags, BitSpan_t out) noexcept -{ - return to_bitspan(Corrade::Containers::arrayView(tags), out); -} - -template -constexpr void task_for_each(Tags const& tags, Tasks const& tasks, FUNC_T&& func) -{ - auto const taskTags2d = task_tags_2d(tags, tasks); - - for (uint32_t const currTask : tasks.m_tasks.bitview().zeros()) - { - func(currTask, taskTags2d[currTask].asContiguous()); - } -} - -bool any_bits_match(BitSpanConst_t lhs, BitSpanConst_t rhs); - -/** - * @brief Enqueue all tasks that contain any specified tag - * - * @param tags [in] Tags - * @param tasks [in] Tasks - * @param rExec [ref] ExecutionContext to record queued tasks into - * @param query [in] Bit positions of tags used to select tasks - */ -void task_enqueue(Tags const& tags, Tasks const& tasks, ExecutionContext &rExec, BitSpanConst_t query); - -void task_extern_set(ExecutionContext &rExec, TagId tag, bool value); - -/** - * @brief List all available tasks that are currently allowed to run - * - * @param tags [in] Tags - * @param tasks [in] Tasks - * @param rExec [in] ExecutionContext indicating tasks available - * @param tasksOut [out] Available tasks as bit positions - */ -void task_list_available(Tags const& tags, Tasks const& tasks, ExecutionContext const& exec, BitSpan_t tasksOut); - -/** - * @brief Mark a task as running in an ExecutionContext - * - * @param tags [in] Tags - * @param tasks [in] Tasks - * @param rExec [ref] ExecutionContext to record running tasks - * @param task [in] Id of task to start running - */ -void task_start(Tags const& tags, Tasks const& tasks, ExecutionContext &rExec, TaskId task); - -/** - * @brief Mark a task as finished in an ExecutionContext - * - * @param tags [in] Tags - * @param tasks [in] Tasks - * @param rExec [ref] ExecutionContext to record running tasks - * @param task [in] Id of task to finish - */ -void task_finish(Tags const& tags, Tasks const& tasks, ExecutionContext &rExec, TaskId task); - -} // namespace osp diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 941c3775..aab037d7 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -25,16 +25,15 @@ #pragma once #include "../keyed_vector.h" +#include "../bitvector.h" #include #include -#include - #include #include -namespace osp +namespace osp::tasks { enum class TaskId : uint32_t { }; @@ -63,22 +62,5 @@ struct Tasks lgrn::IntArrayMultiMap m_semaAcquiredBy; /// Semaphores are acquired by (n) Tasks }; -struct ExecutionContext -{ - - // Number of times each task is queued to run - std::vector m_taskQueuedCounts; - //std::vector m_taskQueuedBits; - - // TODO: Consider multithreading. something something work stealing... - // * Allow multiple threads to search for and execute tasks. Atomic access - // for ExecutionContext? Might be messy to implement. - // * Only allow one thread to search for tasks, assign tasks to other - // threads if they're available before running own task. Another thread - // can take over once it completes its task. May be faster as only one - // thread is modifying ExecutionContext, and easier to implement. - // * Plug into an existing work queue library? - -}; // struct ExecutionContext } // namespace osp diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index 0e20783c..d0b16738 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -24,7 +24,7 @@ */ #include "top_execute.h" #include "top_worker.h" -#include "execute_simple.h" +#include "execute.h" #include "../logging.h" diff --git a/src/osp/tasks/top_execute.h b/src/osp/tasks/top_execute.h index 2dd970de..f921f973 100644 --- a/src/osp/tasks/top_execute.h +++ b/src/osp/tasks/top_execute.h @@ -33,15 +33,13 @@ namespace osp { -void top_run_blocking(Tags const& tags, Tasks const& tasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecutionContext& rExec); +void top_run_blocking(Tasks const& tasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecutionContext& rExec); -void top_enqueue_quick(Tags const& tags, Tasks const& tasks, ExecutionContext& rExec, ArrayView tagsEnq); +void top_enqueue_quick(Tasks const& tasks, ExecutionContext& rExec, ArrayView tagsEnq); -inline void top_enqueue_quick(Tags const& tags, Tasks const& tasks, ExecutionContext& rExec, std::initializer_list tagsEnq) +inline void top_enqueue_quick(Tasks const& tasks, ExecutionContext& rExec, std::initializer_list tagsEnq) { return top_enqueue_quick(tags, tasks, rExec, Corrade::Containers::arrayView(tagsEnq)); } -bool debug_top_print_deadlock(Tags const& tags, Tasks const& tasks, TopTaskDataVec_t const& taskData, ExecutionContext const &rExec); - } // namespace testapp diff --git a/src/osp/tasks/top_session.cpp b/src/osp/tasks/top_session.cpp index bd6d93b5..ef05ee2e 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/osp/tasks/top_session.cpp @@ -24,7 +24,7 @@ */ #include "top_session.h" #include "top_execute.h" -#include "execute_simple.h" +#include "execute.h" #include "tasks.h" #include diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index 7ecdeb62..095f64ff 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include diff --git a/test/tasks/CMakeLists.txt b/test/tasks/CMakeLists.txt index 6ebcb6ec..d9ae2fdf 100644 --- a/test/tasks/CMakeLists.txt +++ b/test/tasks/CMakeLists.txt @@ -26,6 +26,4 @@ PROJECT(test_tasks CXX) ADD_TEST_DIRECTORY(${PROJECT_NAME}) TARGET_LINK_LIBRARIES(test_tasks PRIVATE longeron EnTT::EnTT Magnum::Magnum) -TARGET_SOURCES(test_tasks PRIVATE "${CMAKE_SOURCE_DIR}/src/osp/tasks/builder.cpp") -#TARGET_SOURCES(test_tasks PRIVATE "${CMAKE_SOURCE_DIR}/src/osp/tasks/execute_simple.cpp") - +TARGET_SOURCES(test_tasks PRIVATE "${CMAKE_SOURCE_DIR}/src/osp/tasks/builder.cpp" "${CMAKE_SOURCE_DIR}/src/osp/tasks/execute.cpp") diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 10e592ef..b458c792 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -24,7 +24,7 @@ */ #include #include -//#include +#include #include @@ -33,9 +33,10 @@ #include using namespace osp; +using namespace osp::tasks; template -bool contains(RANGE_T const& range, VALUE_T const& value) +bool contains(RANGE_T const& range, VALUE_T const& value) noexcept { for (auto const& element : range) { @@ -47,101 +48,186 @@ bool contains(RANGE_T const& range, VALUE_T const& value) return false; } -struct World +template +void randomized_singlethreaded_execute(Tasks const& tasks, ExecutionContext& rExec, std::mt19937 &rRand, int maxRuns, RUN_TASK_T && runTask) { - int m_deltaTimeIn{0}; - int m_forces{0}; - int m_positions{0}; - std::set m_canvas; -}; + std::size_t tasksLeft = rExec.m_tasksQueued.count(); -using TaskFunctions_t = KeyedVec>; + for (int i = 0; i < maxRuns; ++i) + { + if (tasksLeft == 0) + { + break; + } -struct CustomTaskBuilder; + // This solution of "pick random '1' bit in a bit vector" is very inefficient + std::size_t const randomTask = *std::next(rExec.m_tasksQueued.ones().begin(), rRand() % tasksLeft); -struct CustomTaskRef : public osp::TaskRefBase -{ - CustomTaskRef& func(std::function in); -}; - -struct CustomTaskBuilder : public osp::TaskBuilderBase -{ - TaskFunctions_t & m_funcs; -}; + FulfillDirty_t const status = runTask(TaskId(randomTask)); + mark_completed_task(tasks, rExec, TaskId(randomTask), status); + int const newTasks = enqueue_dirty(tasks, rExec); -CustomTaskRef& CustomTaskRef::func(std::function in) -{ - m_rBuilder.m_funcs.resize(m_rBuilder.m_taskIds.capacity()); - m_rBuilder.m_funcs[m_taskId] = std::move(in); - return *this; + tasksLeft = tasksLeft - 1 + newTasks; + } } -struct WorldTargets -{ - TargetId timeIn; /// Fire externally when time changes, and world needs to update - TargetId forces; /// Forces need to be calculated for physics - TargetId physics; /// Physics calculations - TargetId renderRequestIn; /// Fire externally when a new frame to render is required - TargetId renderDoneOut; /// Fired when all rendering is finished -}; +//----------------------------------------------------------------------------- -// Single-threaded test against World with order-dependent tasks -TEST(Tasks, SingleThreaded) +// Test multiple tasks fulfilling a single target +TEST(Tasks, BasicParallelSingleThreaded) { - constexpr uint32_t const sc_seed = 69; - constexpr int const sc_repetitions = 32; + // NOTE + // If this was multithreaded, then multiple threads writing to a single container is a bad + // idea. The proper way to do this is to make a vector per-thread. Targets are still + // well-suited for this problem, as these per-thread vectors can all be represented with the + // same TargetId. + + using BasicTraits_t = BasicBuilderTraits&)>; + using Builder_t = BasicTraits_t::Builder; + using TaskFuncVec_t = BasicTraits_t::FuncVec_t; + + constexpr int sc_repetitions = 32; + constexpr int sc_pusherTaskCount = 1337; + constexpr int sc_totalTaskCount = sc_pusherTaskCount + 1; + std::mt19937 randGen(69); + + TaskFuncVec_t functions; + auto builder = Builder_t{functions}; + auto const + [ + inputIn, /// Input int, manually set dirty when it changes + vecClear, /// Vector must be cleared before new stuff can be added to it + vecReady /// Vector is ready, all ints have been added to it + + ] = builder.create_targets<3>(); + + // Clear vector before use + builder.task() + .depends_on({inputIn}) + .fulfills({vecClear}) + .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t + { + rOut.clear(); + return {{0b01}}; + }); + + // Multiple tasks push to the vector + for (int i = 0; i < sc_pusherTaskCount; ++i) + { + builder.task() + .depends_on({vecClear}) + .fulfills({vecReady}) + .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t + { + rOut.push_back(in); + return {{0b01}}; + }); + } + + Tasks const tasks = finalize(std::move(builder)); + ExecutionContext exec; + + exec.resize(tasks); + + int input = 0; + std::vector output; + + // Repeat (with randomness) to test many possible execution orders + for (int i = 0; i < sc_repetitions; ++i) + { + input = 1 + randGen() % 30; - // Setup structs for storing tasks and tags + // Enqueue initial tasks + // This roughly indicates "Time has changed" and "Render requested" + exec.m_targetDirty.set(std::size_t(inputIn)); + enqueue_dirty(tasks, exec); - KeyedVec> functions; + randomized_singlethreaded_execute(tasks, exec, randGen, sc_totalTaskCount, [&functions, &input, &output] (TaskId const task) -> FulfillDirty_t + { + return functions[task](input, output); + }); + + int const sum = std::accumulate(output.begin(), output.end(), 0); - // Create tags and set relationships between them + ASSERT_EQ(sum, input * sc_pusherTaskCount); + } +} - auto builder = CustomTaskBuilder{.m_funcs = functions}; +//----------------------------------------------------------------------------- - auto const tgt = builder.create_targets(); +struct TestWorld +{ + int m_deltaTimeIn{1}; + int m_forces{0}; + int m_positions{0}; + std::set m_canvas; +}; + +struct TestWorldTargets +{ + TargetId timeIn; /// External time input, manually set dirty when time 'changes', and the world needs to update + TargetId forces; /// Forces need to be calculated before physics + TargetId positions; /// Positions calculated by physics task + TargetId renderRequestIn; /// External render request, manually set dirty when a new frame to render is required + TargetId renderDoneOut; /// Fired when all rendering is finished +}; + +// Single-threaded test against TestWorld with order-dependent tasks +TEST(Tasks, BasicSingleThreaded) +{ + using BasicTraits_t = BasicBuilderTraits; + using Builder_t = BasicTraits_t::Builder; + using TaskFuncVec_t = BasicTraits_t::FuncVec_t; + constexpr int sc_repetitions = 32; + std::mt19937 randGen(69); - // Start adding tasks / systems. The order these are added does not matter. + TaskFuncVec_t functions; + auto builder = Builder_t{functions}; + auto const tgt = builder.create_targets(); + // Start adding tasks. The order these are added does not matter. - // Calculate forces needed by the physics update + // Two tasks calculate forces needed by the physics update TaskId const taskA = builder.task() .depends_on({tgt.timeIn}) .fulfills({tgt.forces}) - .func( [] (World& rWorld) + .func( [] (TestWorld& rWorld) -> FulfillDirty_t { rWorld.m_forces += 42 * rWorld.m_deltaTimeIn; + return {{0b01}}; }); - TaskId const taskB = builder.task() .depends_on({tgt.timeIn}) .fulfills({tgt.forces}) - .func([] (World& rWorld) + .func([] (TestWorld& rWorld) -> FulfillDirty_t { rWorld.m_forces += 1337 * rWorld.m_deltaTimeIn; + return {{0b01}}; }); // Main Physics update TaskId const taskC = builder.task() .depends_on({tgt.timeIn, tgt.forces}) - .fulfills({tgt.physics}) - .func([] (World& rWorld) + .fulfills({tgt.positions}) + .func([] (TestWorld& rWorld) -> FulfillDirty_t { EXPECT_EQ(rWorld.m_forces, 1337 + 42); rWorld.m_positions += rWorld.m_forces; rWorld.m_forces = 0; + return {{0b01}}; }); // Draw things moved by physics update. If 'updWorld' wasn't enqueued, then // this will still run, as no 'needPhysics' tasks are incomplete TaskId const taskD = builder.task() - .depends_on({tgt.physics, tgt.renderRequestIn}) + .depends_on({tgt.positions, tgt.renderRequestIn}) .fulfills({tgt.renderDoneOut}) - .func([] (World& rWorld) + .func([] (TestWorld& rWorld) -> FulfillDirty_t { EXPECT_EQ(rWorld.m_positions, 1337 + 42); rWorld.m_canvas.emplace("Physics Cube"); + return {{0b01}}; }); // Draw things unrelated to physics. This is allowed to be the first task @@ -149,86 +235,62 @@ TEST(Tasks, SingleThreaded) TaskId const taskE = builder.task() .depends_on({tgt.renderRequestIn}) .fulfills({tgt.renderDoneOut}) - .func([] (World& rWorld) + .func([] (TestWorld& rWorld) -> FulfillDirty_t { rWorld.m_canvas.emplace("Terrain"); + return {{0b01}}; }); Tasks const tasks = finalize(std::move(builder)); - // Some random checks to assure the graph structure is properly built - EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.timeIn) ].size(), 3); - EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.forces) ].size(), 1); - EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.physics) ].size(), 1); - EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.renderRequestIn) ].size(), 2); - EXPECT_EQ(tasks.m_targetDependents[ uint32_t(tgt.renderDoneOut) ].size(), 0); - EXPECT_TRUE( contains(tasks.m_targetFulfilledBy[uint32_t(tgt.forces)], taskB) ); - EXPECT_FALSE( contains(tasks.m_targetFulfilledBy[uint32_t(tgt.renderDoneOut)], taskC) ); - EXPECT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskA)], tgt.timeIn) ); - EXPECT_FALSE( contains(tasks.m_taskDependOn[uint32_t(taskB)], tgt.physics) ); - EXPECT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskC)], tgt.timeIn) ); - EXPECT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskC)], tgt.forces) ); - EXPECT_TRUE( contains(tasks.m_taskFulfill[uint32_t(taskD)], tgt.renderDoneOut) ); - EXPECT_FALSE( contains(tasks.m_taskFulfill[uint32_t(taskE)], tgt.forces) ); - - -#if 0 - - // Start execution - - ExecutionContext exec; - exec.m_tagIncompleteCounts .resize(tags.m_tags.capacity(), 0); - exec.m_tagRunningCounts .resize(tags.m_tags.capacity(), 0); - exec.m_taskQueuedCounts .resize(tasks.m_tasks.capacity(), 0); - - std::mt19937 gen(sc_seed); - - std::vector tagsToRun(tags.m_tags.vec().size()); - to_bitspan({updWorld, updRender}, tagsToRun); - - World world; + // Random checks to assure the graph structure is properly built + ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.timeIn) ].size(), 3); + ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.forces) ].size(), 1); + ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.positions) ].size(), 1); + ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.renderRequestIn) ].size(), 2); + ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.renderDoneOut) ].size(), 0); + ASSERT_TRUE( contains(tasks.m_targetFulfilledBy[uint32_t(tgt.forces)], taskB) ); + ASSERT_FALSE( contains(tasks.m_targetFulfilledBy[uint32_t(tgt.renderDoneOut)], taskC) ); + ASSERT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskA)], tgt.timeIn) ); + ASSERT_FALSE( contains(tasks.m_taskDependOn[uint32_t(taskB)], tgt.positions) ); + ASSERT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskC)], tgt.timeIn) ); + ASSERT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskC)], tgt.forces) ); + ASSERT_TRUE( contains(tasks.m_taskFulfill[uint32_t(taskD)], tgt.renderDoneOut) ); + ASSERT_FALSE( contains(tasks.m_taskFulfill[uint32_t(taskE)], tgt.forces) ); + + // Execute + + TestWorld world; + ExecutionContext exec; + exec.resize(tasks); // Repeat (with randomness) to test many possible execution orders for (int i = 0; i < sc_repetitions; ++i) { - // Enqueue all tasks tagged with updWorld and updRender - task_enqueue(tags, tasks, exec, tagsToRun); - - // Its best to pass all external information (such as delta time) into - // the systems instead of leaving them to figure it out. - // Enqueuing should be similar to setting a dirty flag. world.m_deltaTimeIn = 1; world.m_positions = 0; world.m_canvas.clear(); - // Run until there's no tasks left to run - while (true) + // Enqueue initial tasks + // This roughly indicates "Time has changed" and "Render requested" + exec.m_targetDirty.set(std::size_t(tgt.timeIn)); + exec.m_targetDirty.set(std::size_t(tgt.renderRequestIn)); + enqueue_dirty(tasks, exec); + + randomized_singlethreaded_execute(tasks, exec, randGen, 5, [&functions, &world] (TaskId const task) -> FulfillDirty_t { - std::vector tasksToRun(tasks.m_tasks.vec().size()); - task_list_available(tags, tasks, exec, tasksToRun); - auto const tasksToRunBits = lgrn::bit_view(tasksToRun); - unsigned int const availableCount = tasksToRunBits.count(); - - if (availableCount == 0) - { - break; - } - - // Choose a random available task - auto const choice = std::uniform_int_distribution{0, availableCount - 1}(gen); - auto const task = TaskId(*std::next(tasksToRunBits.ones().begin(), choice)); - - task_start(tags, tasks, exec, task); - functions.m_taskData[std::size_t(task)](world); - task_finish(tags, tasks, exec, task); - } + return functions[task](world); + }); ASSERT_TRUE(world.m_canvas.contains("Physics Cube")); ASSERT_TRUE(world.m_canvas.contains("Terrain")); } -#endif } // TODO: Multi-threaded test with limits. Actual multithreading isn't needed; // as long as task_start/finish are called at the right times + + + + From bb137fad5e9443264d81bf706553384011705d80 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Tue, 25 Apr 2023 22:54:38 -0700 Subject: [PATCH 12/35] Mess with even more task stuff --- src/osp/tasks/builder.cpp | 138 --------------- src/osp/tasks/builder.h | 76 ++------- src/osp/tasks/execute.cpp | 28 ++-- src/osp/tasks/execute.h | 17 +- src/osp/tasks/tasks.cpp | 158 ++++++++++++++++++ src/osp/tasks/tasks.h | 51 +++++- src/osp/tasks/top_execute.cpp | 119 ++----------- src/osp/tasks/top_execute.h | 10 +- src/osp/tasks/top_session.cpp | 4 +- src/osp/tasks/top_session.h | 75 ++++++--- src/osp/tasks/top_tasks.h | 23 +-- src/osp/tasks/top_utils.h | 76 ++++++++- src/osp/tasks/top_worker.h | 27 +-- .../activescenes/identifiers.h | 20 ++- .../activescenes/scenarios.cpp | 50 +++--- src/test_application/activescenes/scenarios.h | 5 +- .../activescenes/scene_common.cpp | 78 +++++---- .../activescenes/scene_common.h | 6 +- .../activescenes/scene_misc.cpp | 18 +- .../activescenes/scene_misc.h | 12 +- .../activescenes/scene_newton.cpp | 14 +- .../activescenes/scene_newton.h | 20 +-- .../activescenes/scene_physics.cpp | 11 +- .../activescenes/scene_physics.h | 15 +- .../activescenes/scene_renderer.cpp | 28 ++-- .../activescenes/scene_renderer.h | 24 +-- .../activescenes/scene_universe.cpp | 17 +- .../activescenes/scene_universe.h | 13 +- .../activescenes/scene_vehicles.cpp | 30 ++-- .../activescenes/scene_vehicles.h | 29 +--- src/test_application/main.cpp | 14 +- test/tasks/CMakeLists.txt | 2 +- test/tasks/main.cpp | 67 ++++---- 33 files changed, 626 insertions(+), 649 deletions(-) delete mode 100644 src/osp/tasks/builder.cpp create mode 100644 src/osp/tasks/tasks.cpp diff --git a/src/osp/tasks/builder.cpp b/src/osp/tasks/builder.cpp deleted file mode 100644 index 068d9dc6..00000000 --- a/src/osp/tasks/builder.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Open Space Program - * Copyright © 2019-2023 Open Space Program Project - * - * MIT License - * - * 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 "builder.h" - -namespace osp::tasks -{ - -Tasks finalize(TaskBuilderData && data) -{ - using TargetCounts = TaskBuilderData::TargetCounts; - using TaskCounts = TaskBuilderData::TaskCounts; - using TargetInt = Tasks::TargetInt; - using TaskInt = Tasks::TaskInt; - - Tasks out; - - KeyedVec taskCounts; - KeyedVec targetCounts; - - taskCounts .resize(data.m_taskIds .capacity()); - targetCounts.resize(data.m_targetIds.capacity()); - - // Count connections - - for (auto const [task, target] : data.m_targetDependEdges) - { - ++ taskCounts[task] .m_dependingOn; - ++ targetCounts[target].m_dependents; - } - - for (auto const [task, target] : data.m_targetFulfillEdges) - { - ++ taskCounts[task] .m_fulfills; - ++ targetCounts[target].m_fulfilledBy; - } - - // Allocate - - out.m_taskDependOn .ids_reserve (data.m_taskIds.capacity()); - out.m_taskDependOn .data_reserve (data.m_targetDependEdges.size()); - out.m_targetDependents .ids_reserve (data.m_targetIds.capacity()); - out.m_targetDependents .data_reserve (data.m_targetDependEdges.size()); - out.m_taskFulfill .ids_reserve (data.m_taskIds.capacity()); - out.m_taskFulfill .data_reserve (data.m_targetFulfillEdges.size()); - out.m_targetFulfilledBy .ids_reserve (data.m_targetIds.capacity()); - out.m_targetFulfilledBy .data_reserve (data.m_targetFulfillEdges.size()); - - // Reserve partitions - - for (Tasks::TaskInt const task : data.m_taskIds.bitview().zeros()) - { - if (std::size_t const size = taskCounts[TaskId(task)].m_dependingOn; - size != 0) - { - out.m_taskDependOn.emplace(task, size); - } - if (std::size_t const size = taskCounts[TaskId(task)].m_fulfills; - size != 0) - { - out.m_taskFulfill.emplace(task, size); - } - } - - for (Tasks::TargetInt const target : data.m_targetIds.bitview().zeros()) - { - if (std::size_t const size = targetCounts[TargetId(target)].m_dependents; - size != 0) - { - out.m_targetDependents.emplace(target, size); - } - - if (std::size_t const size = targetCounts[TargetId(target)].m_fulfilledBy; - size != 0) - { - out.m_targetFulfilledBy.emplace(target, size); - } - } - - // Place - - for (auto const [task, target] : data.m_targetDependEdges) - { - auto const dependOn = lgrn::Span (out.m_taskDependOn[TaskInt(task)]); - auto const dependents = lgrn::Span (out.m_targetDependents[TargetInt(target)]); - - dependOn [dependOn.size() - taskCounts[task].m_dependingOn] = target; - dependents [dependents.size() - targetCounts[target].m_dependents] = task; - - --taskCounts[task] .m_dependingOn; - --targetCounts[target].m_dependents; - } - - for (auto const [task, target] : data.m_targetFulfillEdges) - { - auto const fulfills = lgrn::Span (out.m_taskFulfill[TaskInt(task)]); - auto const fulfilledBy = lgrn::Span (out.m_targetFulfilledBy[TargetInt(target)]); - - fulfills [fulfills.size() - taskCounts[task].m_fulfills] = target; - fulfilledBy [fulfilledBy.size() - targetCounts[target].m_fulfilledBy] = task; - - -- taskCounts[task] .m_fulfills; - -- targetCounts[target].m_fulfilledBy; - } - - out.m_taskIds = std::move(data.m_taskIds); - out.m_targetIds = std::move(data.m_targetIds); - out.m_semaIds = std::move(data.m_semaIds); - out.m_semaLimits = std::move(data.m_semaLimits); - - return out; -} - - - -} // namespace osp - diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 936ff96d..1fbccdc4 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -30,67 +30,24 @@ #include #include -#include -#include - #include #include #include -namespace osp::tasks -{ - -struct TaskBuilderData +namespace osp { - struct TaskTargetPair - { - TaskId m_task; - TargetId m_target; - }; - - struct TaskSemaphorePair - { - TaskId m_task; - SemaphoreId m_sema; - }; - - struct TaskCounts - { - unsigned int m_dependingOn {0}; - unsigned int m_fulfills {0}; - unsigned int m_acquires {0}; - }; - - struct TargetCounts - { - unsigned int m_dependents {0}; - unsigned int m_fulfilledBy {0}; - }; - - lgrn::IdRegistryStl m_taskIds; - lgrn::IdRegistryStl m_targetIds; - lgrn::IdRegistryStl m_semaIds; - KeyedVec m_semaLimits; - - std::vector m_targetDependEdges; - std::vector m_targetFulfillEdges; - std::vector m_semaphoreEdges; - -}; // TaskBuilderData /** * @brief A convenient interface for setting up Tasks and required task data */ template -struct TaskBuilderBase : TaskBuilderData +struct TaskBuilderBase { -public: - TASKREF_T task() { - TaskId const taskId = m_taskIds.create(); + TaskId const taskId = m_rTasks.m_taskIds.create(); - std::size_t const capacity = m_taskIds.capacity(); + std::size_t const capacity = m_rTasks.m_taskIds.capacity(); return task(taskId); }; @@ -111,7 +68,7 @@ struct TaskBuilderBase : TaskBuilderData std::array out; - m_targetIds.create(out.begin(), out.end()); + m_rTasks.m_targetIds.create(out.begin(), out.end()); return reinterpret_cast(*out.data()); } @@ -120,10 +77,13 @@ struct TaskBuilderBase : TaskBuilderData std::array create_targets() { std::array out; - m_targetIds.create(out.begin(), out.end()); + m_rTasks.m_targetIds.create(out.begin(), out.end()); return out; } + Tasks& m_rTasks; + TaskEdges& m_rEdges; + }; // class TaskBuilderBase @@ -143,7 +103,7 @@ struct TaskRefBase { for (TargetId const target : targets) { - m_rBuilder.m_targetDependEdges.push_back({m_taskId, target}); + m_rBuilder.m_rEdges.m_targetDependEdges.push_back({m_taskId, target}); } return static_cast(*this); } @@ -157,7 +117,7 @@ struct TaskRefBase { for (TargetId const target : targets) { - m_rBuilder.m_targetFulfillEdges.push_back({m_taskId, target}); + m_rBuilder.m_rEdges.m_targetFulfillEdges.push_back({m_taskId, target}); } return static_cast(*this); } @@ -187,27 +147,25 @@ struct BasicBuilderTraits struct Builder : public TaskBuilderBase { - Builder(FuncVec_t& funcs) - : m_funcs{funcs} + Builder(Tasks& rTasks, TaskEdges& rEdges, FuncVec_t& rFuncs) + : TaskBuilderBase{ rTasks, rEdges } + , m_rFuncs{rFuncs} { } Builder(Builder const& copy) = delete; Builder(Builder && move) = default; Builder& operator=(Builder const& copy) = delete; - Builder& operator=(Builder && move) = default; - FuncVec_t & m_funcs; + FuncVec_t & m_rFuncs; }; }; template typename BasicBuilderTraits::Ref& BasicBuilderTraits::Ref::func(FUNC_T && in) { - this->m_rBuilder.m_funcs.resize(this->m_rBuilder.m_taskIds.capacity()); - this->m_rBuilder.m_funcs[this->m_taskId] = std::move(in); + this->m_rBuilder.m_rFuncs.resize(this->m_rBuilder.m_rTasks.m_taskIds.capacity()); + this->m_rBuilder.m_rFuncs[this->m_taskId] = std::move(in); return *this; } -Tasks finalize(TaskBuilderData && data); - } // namespace osp diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 53e498c7..608466c5 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -26,17 +26,17 @@ #include "execute.h" -namespace osp::tasks +namespace osp { -bool try_enqueue_task(Tasks const& tasks, ExecutionContext &rExec, TaskId const task) noexcept +bool try_enqueue_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task) noexcept { if (rExec.m_tasksQueued.test(std::size_t(task))) { return false; // task already queued } - for (TargetId const dependOn : tasks.m_taskDependOn[std::size_t(task)]) + for (TargetId const dependOn : graph.m_taskDependOn[std::size_t(task)]) { if ( rExec.m_targetPendingCount[dependOn] != 0 || rExec.m_targetWillBePending.test(std::size_t(dependOn)) ) @@ -45,12 +45,12 @@ bool try_enqueue_task(Tasks const& tasks, ExecutionContext &rExec, TaskId const } } - for (TargetId const dependOn : tasks.m_taskDependOn[std::size_t(task)]) + for (TargetId const dependOn : graph.m_taskDependOn[std::size_t(task)]) { ++ rExec.m_targetInUseCount[dependOn]; } - for (TargetId const fulfill : tasks.m_taskFulfill[std::size_t(task)]) + for (TargetId const fulfill : graph.m_taskFulfill[std::size_t(task)]) { ++ rExec.m_targetPendingCount[fulfill]; } @@ -60,14 +60,14 @@ bool try_enqueue_task(Tasks const& tasks, ExecutionContext &rExec, TaskId const return true; } -int enqueue_dirty(Tasks const& tasks, ExecutionContext &rExec) noexcept +int enqueue_dirty(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec) noexcept { // Find out which targets will be pending for (std::size_t const target : rExec.m_targetDirty.ones()) { - for (TaskId const dependent : tasks.m_targetDependents[target]) + for (TaskId const dependent : graph.m_targetDependents[target]) { - for (TargetId const fulfill : tasks.m_taskFulfill[std::size_t(dependent)]) + for (TargetId const fulfill : graph.m_taskFulfill[std::size_t(dependent)]) { rExec.m_targetWillBePending.set(std::size_t(fulfill)); } @@ -78,9 +78,9 @@ int enqueue_dirty(Tasks const& tasks, ExecutionContext &rExec) noexcept for (std::size_t const target : rExec.m_targetDirty.ones()) { - for (TaskId const dependent : tasks.m_targetDependents[target]) + for (TaskId const dependent : graph.m_targetDependents[target]) { - tasksEnqueued += int(try_enqueue_task(tasks, rExec, dependent)); + tasksEnqueued += int(try_enqueue_task(tasks, graph, rExec, dependent)); } } rExec.m_targetWillBePending.reset(); @@ -89,12 +89,12 @@ int enqueue_dirty(Tasks const& tasks, ExecutionContext &rExec) noexcept return tasksEnqueued; } -void mark_completed_task(Tasks const& tasks, ExecutionContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept +void mark_completed_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept { LGRN_ASSERTMV(rExec.m_tasksQueued.test(std::size_t(task)), "Task must be queued to have been allowed to run", std::size_t(task)); - for (TargetId const dependOn : tasks.m_taskDependOn[std::size_t(task)]) + for (TargetId const dependOn : graph.m_taskDependOn[std::size_t(task)]) { -- rExec.m_targetInUseCount[dependOn]; @@ -104,7 +104,7 @@ void mark_completed_task(Tasks const& tasks, ExecutionContext &rExec, TaskId con } } - auto const taskFulfill = lgrn::Span(tasks.m_taskFulfill[std::size_t(task)]); + auto const taskFulfill = lgrn::Span(graph.m_taskFulfill[std::size_t(task)]); for (int i = 0; i < taskFulfill.size(); ++i) { @@ -125,4 +125,4 @@ void mark_completed_task(Tasks const& tasks, ExecutionContext &rExec, TaskId con } -} // namespace osp::tasks +} // namespace osp diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 032a1276..44382bc9 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -26,21 +26,18 @@ #include "tasks.h" +#include "../bitvector.h" + #include #include #include #include -namespace osp::tasks +namespace osp { -/** - * @brief Bitset returned by tasks to determine which fulfill targets should be marked dirty - */ -using FulfillDirty_t = lgrn::BitView>; - -struct ExecutionContext +struct ExecContext { void resize(Tasks const& tasks) { @@ -72,10 +69,10 @@ struct ExecutionContext }; // struct ExecutionContext -int enqueue_dirty(Tasks const& tasks, ExecutionContext &rExec) noexcept; +int enqueue_dirty(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec) noexcept; -void mark_completed_task(Tasks const& tasks, ExecutionContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept; +void mark_completed_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept; -} // namespace osp::tasks +} // namespace osp diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp new file mode 100644 index 00000000..101d5c03 --- /dev/null +++ b/src/osp/tasks/tasks.cpp @@ -0,0 +1,158 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * 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 "tasks.h" + +namespace osp +{ + +struct TaskCounts +{ + unsigned int m_dependingOn {0}; + unsigned int m_fulfills {0}; + unsigned int m_acquires {0}; +}; + +struct TargetCounts +{ + unsigned int m_dependents {0}; + unsigned int m_fulfilledBy {0}; +}; + + +ExecGraph make_exec_graph(Tasks const& tasks, ArrayView const data) +{ + using TargetInt = Tasks::TargetInt; + using TaskInt = Tasks::TaskInt; + + ExecGraph out; + + KeyedVec taskCounts; + KeyedVec targetCounts; + + taskCounts .resize(tasks.m_taskIds .capacity()); + targetCounts.resize(tasks.m_targetIds.capacity()); + + // Count connections + + std::size_t totalDependEdges = 0; + std::size_t totalFulfillEdges = 0; + + for (TaskEdges const* pEdges : data) + { + totalDependEdges += pEdges->m_targetDependEdges.size(); + totalFulfillEdges += pEdges->m_targetFulfillEdges.size(); + + for (auto const [task, target] : pEdges->m_targetDependEdges) + { + ++ taskCounts[task] .m_dependingOn; + ++ targetCounts[target].m_dependents; + } + + for (auto const [task, target] : pEdges->m_targetFulfillEdges) + { + ++ taskCounts[task] .m_fulfills; + ++ targetCounts[target].m_fulfilledBy; + } + } + + // Allocate + + out.m_taskDependOn .ids_reserve (tasks.m_taskIds.capacity()); + out.m_taskDependOn .data_reserve (totalDependEdges); + out.m_targetDependents .ids_reserve (tasks.m_targetIds.capacity()); + out.m_targetDependents .data_reserve (totalDependEdges); + out.m_taskFulfill .ids_reserve (tasks.m_taskIds.capacity()); + out.m_taskFulfill .data_reserve (totalFulfillEdges); + out.m_targetFulfilledBy .ids_reserve (tasks.m_targetIds.capacity()); + out.m_targetFulfilledBy .data_reserve (totalFulfillEdges); + + // Reserve partitions + + for (Tasks::TaskInt const task : tasks.m_taskIds.bitview().zeros()) + { + if (std::size_t const size = taskCounts[TaskId(task)].m_dependingOn; + size != 0) + { + out.m_taskDependOn.emplace(task, size); + } + if (std::size_t const size = taskCounts[TaskId(task)].m_fulfills; + size != 0) + { + out.m_taskFulfill.emplace(task, size); + } + } + + for (Tasks::TargetInt const target : tasks.m_targetIds.bitview().zeros()) + { + if (std::size_t const size = targetCounts[TargetId(target)].m_dependents; + size != 0) + { + out.m_targetDependents.emplace(target, size); + } + + if (std::size_t const size = targetCounts[TargetId(target)].m_fulfilledBy; + size != 0) + { + out.m_targetFulfilledBy.emplace(target, size); + } + } + + // Place connections into allocated partitions + + for (TaskEdges const* pEdges : data) + { + for (auto const [task, target] : pEdges->m_targetDependEdges) + { + auto const dependOn = lgrn::Span (out.m_taskDependOn[TaskInt(task)]); + auto const dependents = lgrn::Span (out.m_targetDependents[TargetInt(target)]); + + dependOn [dependOn.size() - taskCounts[task].m_dependingOn] = target; + dependents [dependents.size() - targetCounts[target].m_dependents] = task; + + --taskCounts[task] .m_dependingOn; + --targetCounts[target].m_dependents; + } + + for (auto const [task, target] : pEdges->m_targetFulfillEdges) + { + auto const fulfills = lgrn::Span (out.m_taskFulfill[TaskInt(task)]); + auto const fulfilledBy = lgrn::Span (out.m_targetFulfilledBy[TargetInt(target)]); + + fulfills [fulfills.size() - taskCounts[task].m_fulfills] = target; + fulfilledBy [fulfilledBy.size() - targetCounts[target].m_fulfilledBy] = task; + + -- taskCounts[task] .m_fulfills; + -- targetCounts[target].m_fulfilledBy; + } + } + + + return out; +} + + + +} // namespace osp + diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index aab037d7..ea02ee71 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -25,7 +25,7 @@ #pragma once #include "../keyed_vector.h" -#include "../bitvector.h" +#include "../types.h" #include #include @@ -33,7 +33,7 @@ #include #include -namespace osp::tasks +namespace osp { enum class TaskId : uint32_t { }; @@ -51,16 +51,51 @@ struct Tasks lgrn::IdRegistryStl m_semaIds; KeyedVec m_semaLimits; +}; + +struct TaskEdges +{ + struct TargetPair + { + TaskId m_task; + TargetId m_target; + }; + + struct SemaphorePair + { + TaskId m_task; + SemaphoreId m_sema; + }; - lgrn::IntArrayMultiMap m_taskDependOn; /// Tasks depend on (n) Targets - lgrn::IntArrayMultiMap m_targetDependents; /// Targets have (n) Tasks that depend on it + std::vector m_targetDependEdges; + std::vector m_targetFulfillEdges; + std::vector m_semaphoreEdges; +}; + +struct ExecGraph +{ + lgrn::IntArrayMultiMap m_taskDependOn; /// Tasks depend on (n) Targets + lgrn::IntArrayMultiMap m_targetDependents; /// Targets have (n) Tasks that depend on it - lgrn::IntArrayMultiMap m_taskFulfill; /// Tasks fulfill (n) Targets - lgrn::IntArrayMultiMap m_targetFulfilledBy; /// Targets are fulfilled by (n) Tasks + lgrn::IntArrayMultiMap m_taskFulfill; /// Tasks fulfill (n) Targets + lgrn::IntArrayMultiMap m_targetFulfilledBy; /// Targets are fulfilled by (n) Tasks - lgrn::IntArrayMultiMap m_taskAcquire; /// Tasks acquire (n) Semaphores - lgrn::IntArrayMultiMap m_semaAcquiredBy; /// Semaphores are acquired by (n) Tasks + lgrn::IntArrayMultiMap m_taskAcquire; /// Tasks acquire (n) Semaphores + lgrn::IntArrayMultiMap m_semaAcquiredBy; /// Semaphores are acquired by (n) Tasks }; +/** + * @brief Bitset returned by tasks to determine which fulfill targets should be marked dirty + */ +using FulfillDirty_t = lgrn::BitView>; + + +ExecGraph make_exec_graph(Tasks const& tasks, ArrayView data); + +inline ExecGraph make_exec_graph(Tasks const& tasks, std::initializer_list data) +{ + return make_exec_graph(tasks, arrayView(data)); +} + } // namespace osp diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index d0b16738..de03707a 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -36,42 +36,28 @@ #include #include -using Corrade::Containers::ArrayView; -using Corrade::Containers::StridedArrayView2D; -using Corrade::Containers::arrayView; - namespace osp { -void top_run_blocking(Tags const& tags, Tasks const& tasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecutionContext& rExec) +void top_run_blocking(Tasks const& tasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec) { std::vector topDataRefs; - std::vector enqueue(tags.m_tags.vec().size()); - - std::vector tasksToRun(tasks.m_tasks.vec().size()); - - // Run until there's no tasks left to run while (true) { - std::fill(std::begin(tasksToRun), std::end(tasksToRun), 0u); - task_list_available(tags, tasks, rExec, tasksToRun); - - auto const tasksToRunBits = lgrn::bit_view(tasksToRun); - unsigned int const availableCount = tasksToRunBits.count(); + // Choose first available task + auto const enqueuedTasks = rExec.m_tasksQueued.ones(); + auto const taskIt = enqueuedTasks.begin(); - if (availableCount == 0) + if (taskIt == enqueuedTasks.end()) { break; } - // Choose first available task - auto const task = TaskId(*tasksToRunBits.ones().begin()); - - task_start(tags, tasks, rExec, task); + auto const task = TaskId(*taskIt); - TopTask &rTopTask = rTaskData.m_taskData.at(std::size_t(task)); + TopTask &rTopTask = rTaskData[task]; topDataRefs.clear(); topDataRefs.reserve(rTopTask.m_dataUsed.size()); @@ -86,100 +72,21 @@ void top_run_blocking(Tags const& tags, Tasks const& tasks, TopTaskDataVec_t& rT bool enqueueHappened = false; // Task actually runs here. Results are not yet used for anything. - rTopTask.m_func(WorkerContext{{}, {enqueue}, enqueueHappened}, topDataRefs); - - if (enqueueHappened) - { - task_enqueue(tags, tasks, rExec, enqueue); - std::fill(std::begin(enqueue), std::end(enqueue), 0u); - } + FulfillDirty_t const status = rTopTask.m_func(WorkerContext{}, topDataRefs); - task_finish(tags, tasks, rExec, task); + mark_completed_task(tasks, rExec, task, status); } } -void top_enqueue_quick(Tags const& tags, Tasks const& tasks, ExecutionContext& rExec, ArrayView tagsEnq) -{ - std::vector tagsToRun(tags.m_tags.vec().size()); - to_bitspan(tagsEnq, tagsToRun); - task_enqueue(tags, tasks, rExec, tagsToRun); -} - -static void check_task_dependencies(Tags const& tags, Tasks const& tasks, TopTaskDataVec_t const& taskData, ExecutionContext const &rExec, std::vector &rPath, uint32_t const task, bool& rGood) +void top_enqueue_quick(Tasks const& tasks, ExecContext& rExec, ArrayView enqueue) { - if (rExec.m_taskQueuedCounts.at(task) == 0) - { - return; // Only consider queued tasks - } - - rPath.push_back(task); - - auto const taskTags2d = task_tags_2d(tags, tasks); - auto const tagDepends2d = tag_depends_2d(tags); - - std::vector dependencyTagInts(tags.m_tags.vec().size(), 0x0); - auto dependencyTagsBits = lgrn::bit_view(dependencyTagInts); - - // Loop through each of this task's tags to get dependent tags - auto const taskTagsInts = taskTags2d[task].asContiguous(); - auto const view = lgrn::bit_view(taskTagsInts); - for (uint32_t const currTag : view.ones()) - { - auto const currTagDepends = tagDepends2d[currTag].asContiguous(); - - for (TagId const dependTag : currTagDepends) - { - if (dependTag == lgrn::id_null()) - { - break; - } - - dependencyTagsBits.set(std::size_t(dependTag)); - } - } - - // Get all tasks that contain any of the dependent tags - for (uint32_t const currTask : tasks.m_tasks.bitview().zeros()) + for (TargetId const target : enqueue) { - if (any_bits_match(taskTags2d[currTask].asContiguous(), dependencyTagInts)) - { - auto const &last = std::end(rPath); - if (last == std::find(std::begin(rPath), last, currTask)) - { - check_task_dependencies(tags, tasks, taskData, rExec, rPath, currTask, rGood); - } - else - { - rGood = false; - std::ostringstream ss; - ss << "TopTask Circular Dependency Detected!\n"; - - for (uint32_t const pathTask : rPath) - { - ss << "* [" << pathTask << "]: " << taskData.m_taskData[pathTask].m_debugName << "\n"; - } - ss << "* Loops back to [" << currTask << "]\n"; - OSP_LOG_ERROR("{}", ss.str()); - } - } + rExec.m_targetDirty.set(std::size_t(target)); } - rPath.pop_back(); + enqueue_dirty(tasks, rExec); } -bool debug_top_print_deadlock(Tags const& tags, Tasks const& tasks, TopTaskDataVec_t const& taskData, ExecutionContext const &rExec) -{ - std::vector path; - - bool good = true; - - // Iterate all tasks - for (uint32_t const currTask : tasks.m_tasks.bitview().zeros()) - { - check_task_dependencies(tags, tasks, taskData, rExec, path, currTask, good); - } - - return good; -} } // namespace testapp diff --git a/src/osp/tasks/top_execute.h b/src/osp/tasks/top_execute.h index f921f973..7908e234 100644 --- a/src/osp/tasks/top_execute.h +++ b/src/osp/tasks/top_execute.h @@ -24,6 +24,7 @@ */ #pragma once +#include "execute.h" #include "tasks.h" #include "top_tasks.h" @@ -32,14 +33,13 @@ namespace osp { +void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec); -void top_run_blocking(Tasks const& tasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecutionContext& rExec); +void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, ArrayView enqueue); -void top_enqueue_quick(Tasks const& tasks, ExecutionContext& rExec, ArrayView tagsEnq); - -inline void top_enqueue_quick(Tasks const& tasks, ExecutionContext& rExec, std::initializer_list tagsEnq) +inline void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, std::initializer_list enqueue) { - return top_enqueue_quick(tags, tasks, rExec, Corrade::Containers::arrayView(tagsEnq)); + return top_enqueue_quick(tasks, graph, rExec, Corrade::Containers::arrayView(enqueue)); } } // namespace testapp diff --git a/src/osp/tasks/top_session.cpp b/src/osp/tasks/top_session.cpp index ef05ee2e..e64af6db 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/osp/tasks/top_session.cpp @@ -40,8 +40,9 @@ using Corrade::Containers::arrayView; namespace osp { -void top_close_session(Tags& rTags, Tasks& rTasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecutionContext& rExec, ArrayView sessions) +void top_close_session(Tasks& rTasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, ArrayView sessions) { +#if 0 // Run cleanup tasks { // Accumulate together all cleanup tags from all sessons @@ -106,6 +107,7 @@ void top_close_session(Tags& rTags, Tasks& rTasks, TopTaskDataVec_t& rTaskData, } } } +#endif } } // namespace testapp diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index 32472d06..aa7f876c 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -24,14 +24,16 @@ */ #pragma once -#include "top_tasks.h" -#include "top_utils.h" +#include "builder.h" +#include "execute.h" #include "tasks.h" +#include "top_tasks.h" #include #include #include +#include #include #include @@ -41,10 +43,8 @@ #define OSP_STRUCT_BIND_B(x) x #define OSP_STRUCT_BIND_A(...) OSP_STRUCT_BIND_B(OSP_STRUCT_BIND_C(__VA_ARGS__)) -#define OSP_SESSION_UNPACK_TAGS(session, name) OSP_STRUCT_BIND_A(osp::unpack, (session).m_tagIds, OSP_TAGS_##name); -#define OSP_SESSION_UNPACK_DATA(session, name) OSP_STRUCT_BIND_A(osp::unpack, (session).m_dataIds, OSP_DATA_##name); +#define OSP_SESSION_UNPACK_DATA(session, name) OSP_STRUCT_BIND_A(osp::unpack, (session).m_data, OSP_DATA_##name); -#define OSP_SESSION_ACQUIRE_TAGS(session, tags, name) OSP_STRUCT_BIND_A((session).acquire_tags, (tags), OSP_TAGS_##name); #define OSP_SESSION_ACQUIRE_DATA(session, topData, name) OSP_STRUCT_BIND_A((session).acquire_data, (topData), OSP_DATA_##name); namespace osp @@ -64,37 +64,72 @@ struct Session { std::array out; top_reserve(topData, 0, std::begin(out), std::end(out)); - m_dataIds.assign(std::begin(out), std::end(out)); + m_data.assign(std::begin(out), std::end(out)); return out; } - template - std::array acquire_tags(Tags &rTags) + template + TGT_STRUCT_T create_targets(TaskBuilderData &rBuilder) { - std::array out; - rTags.m_tags.create(std::begin(out), std::end(out)); - m_tagIds.assign(std::begin(out), std::end(out)); - return out; + static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); + + std::type_info const& info = typeid(TGT_STRUCT_T); + m_targetStructHash = info.hash_code(); + m_targetStructName = info.name(); + + m_targets.resize(count); + + rBuilder.m_targetIds.create(m_targets.begin(), m_targets.end()); + + return reinterpret_cast(*m_targets.data()); + } + + template + TGT_STRUCT_T get_targets() const + { + static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); + + std::type_info const& info = typeid(TGT_STRUCT_T); + LGRN_ASSERTMV(m_targetStructHash == info.hash_code(), + "get_targets must use the same struct given to create_targets", + m_targetStructHash, m_targetStructName, + info.hash_code(), info.name()); } TaskId& task() { - return m_taskIds.emplace_back(lgrn::id_null()); + return m_tasks.emplace_back(lgrn::id_null()); } - std::vector m_dataIds; - std::vector m_tagIds; - std::vector m_taskIds; + std::vector m_data; + std::vector m_targets; + std::vector m_tasks; + + TargetId m_tgCleanupEvt{lgrn::id_null()}; - TagId m_tgCleanupEvt{lgrn::id_null()}; + std::size_t m_targetStructHash{0}; + std::string m_targetStructName; }; -using Sessions_t = std::vector; +template +std::vector to_target_vec(TGT_STRUCT_T const& targets) +{ + static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); + + TargetId const* pData = reinterpret_cast(std::addressof(targets)); + + return {pData, pData + count}; +} + +using Sessions_t = std::vector; /** - * @brief Close sessions, delete all their associated TopData, Tasks, and Tags. + * @brief Close sessions, delete all their associated TopData, Tasks, and Targets. */ -void top_close_session(Tags& rTags, Tasks& rTasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecutionContext& rExec, ArrayView sessions); +void top_close_session(Tasks& rTasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecutionContext& rExec, ArrayView sessions); } // namespace osp diff --git a/src/osp/tasks/top_tasks.h b/src/osp/tasks/top_tasks.h index d8a38dd1..c97d6938 100644 --- a/src/osp/tasks/top_tasks.h +++ b/src/osp/tasks/top_tasks.h @@ -37,26 +37,11 @@ namespace osp struct TopTask { - std::string m_debugName; - std::vector m_dataUsed; - TopTaskFunc_t m_func{nullptr}; + std::string m_debugName; + std::vector m_dataUsed; + TopTaskFunc_t m_func{nullptr}; }; -inline void task_data(TaskDataVec &rData, TaskId const task, std::string_view debugName, std::initializer_list dataUsed, TopTaskFunc_t func) -{ - rData.m_taskData.resize( - std::max(rData.m_taskData.size(), std::size_t(task) + 1)); - auto &rTopTask = rData.m_taskData[std::size_t(task)]; - rTopTask.m_debugName = debugName; - rTopTask.m_dataUsed = dataUsed; - rTopTask.m_func = func; -} - -inline void task_data(TaskDataVec &rData, TaskId const task, std::initializer_list dataUsed, TopTaskFunc_t func) -{ - task_data(rData, task, "Untitled Top Task", dataUsed, func); -} - -using TopTaskDataVec_t = TaskDataVec; +using TopTaskDataVec_t = KeyedVec; } // namespace osp diff --git a/src/osp/tasks/top_utils.h b/src/osp/tasks/top_utils.h index c01f12b0..a9bad063 100644 --- a/src/osp/tasks/top_utils.h +++ b/src/osp/tasks/top_utils.h @@ -25,6 +25,8 @@ #pragma once #include "tasks.h" +#include "builder.h" +#include "top_tasks.h" #include "top_worker.h" #include @@ -33,7 +35,6 @@ #include #include #include -#include namespace osp { @@ -101,7 +102,7 @@ template return entt::any_cast(topData[id]); } - +//----------------------------------------------------------------------------- template struct wrap_args_trait @@ -127,14 +128,14 @@ struct wrap_args_trait } template - static TopTaskStatus wrapped_task([[maybe_unused]] WorkerContext ctx, ArrayView topData) noexcept + static FulfillDirty_t wrapped_task([[maybe_unused]] WorkerContext ctx, ArrayView topData) noexcept { if constexpr (std::is_void_v) { cast_args(topData, ctx, std::make_index_sequence{}); - return TopTaskStatus::Success; + return {{0xFFFFFFFFFFFFFFFFul}}; // All fulfilled targets set dirty } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { return cast_args(topData, ctx, std::make_index_sequence{}); } @@ -176,5 +177,70 @@ constexpr TopTaskFunc_t wrap_args(FUNC_T funcArg) return wrap_args_trait::unpack(functionPtr); } +//----------------------------------------------------------------------------- + +struct TopTaskBuilder; + +struct TopTaskRef : public TaskRefBase +{ + TopTaskRef& name(std::string_view debugName); + TopTaskRef& data(std::initializer_list dataUsed); + + template + TopTaskRef& func(FUNC_T funcArg); + TopTaskRef& func_raw(TopTaskFunc_t func); + + template + TopTaskRef& push_to(CONTAINER_T& rContainer); +}; + +struct TopTaskBuilder : public TaskBuilderBase +{ + TopTaskBuilder(TopTaskDataVec_t& data) + : m_rData{data} + { } + TopTaskBuilder(TopTaskBuilder const& copy) = delete; + TopTaskBuilder(TopTaskBuilder && move) = default; + + TopTaskBuilder& operator=(TopTaskBuilder const& copy) = delete; + + TopTaskDataVec_t & m_rData; +}; + +TopTaskRef& TopTaskRef::name(std::string_view debugName) +{ + m_rBuilder.m_rData.resize(m_rBuilder.m_taskIds.capacity()); + m_rBuilder.m_rData[m_taskId].m_debugName = debugName; + return *this; +} + +TopTaskRef& TopTaskRef::data(std::initializer_list dataUsed) +{ + m_rBuilder.m_rData.resize(m_rBuilder.m_taskIds.capacity()); + m_rBuilder.m_rData[m_taskId].m_dataUsed = dataUsed; + return *this; +} + +template +TopTaskRef& TopTaskRef::func(FUNC_T funcArg) +{ + m_rBuilder.m_rData.resize(m_rBuilder.m_taskIds.capacity()); + m_rBuilder.m_rData[m_taskId].m_func = wrap_args(funcArg); + return *this; +} + +TopTaskRef& TopTaskRef::func_raw(TopTaskFunc_t func) +{ + m_rBuilder.m_rData.resize(m_rBuilder.m_taskIds.capacity()); + m_rBuilder.m_rData[m_taskId].m_func = func; + return *this; +} + +template +TopTaskRef& TopTaskRef::push_to(CONTAINER_T& rContainer) +{ + rContainer.push_back(m_taskId); + return *this; +} } // namespace osp diff --git a/src/osp/tasks/top_worker.h b/src/osp/tasks/top_worker.h index e023ba88..33d27fb3 100644 --- a/src/osp/tasks/top_worker.h +++ b/src/osp/tasks/top_worker.h @@ -24,18 +24,19 @@ */ #pragma once -#include +#include "../types.h" #include +#include + +#include #include namespace osp { -using bit_int_t = uint64_t; - -using Corrade::Containers::ArrayView; +using FulfillDirty_t = lgrn::BitView>; using TopDataId = uint32_t; using TopDataIds_t = std::initializer_list; @@ -44,25 +45,9 @@ struct Reserved {}; struct WorkerContext { - struct LimitSlot - { - uint32_t m_tag; - int m_slot; - }; - - Corrade::Containers::ArrayView m_limitSlots; - Corrade::Containers::ArrayView m_enqueue; - bool & m_rEnqueueHappened; -}; - -enum class TopTaskStatus : uint8_t -{ - Success = 0, - Failed = 1, - DidNothing = 2 }; -using TopTaskFunc_t = TopTaskStatus(*)(WorkerContext, ArrayView) noexcept; +using TopTaskFunc_t = FulfillDirty_t(*)(WorkerContext, ArrayView) noexcept; } // namespace osp diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 6e88b61c..7e98a0c4 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -24,6 +24,11 @@ */ #pragma once +#include + +namespace testapp +{ + // Identifiers made for OSP_ACQUIRE_* and OSP_UNPACK_* macros // Used to declare variable names for TopDataIds and TagIds // #define OSP_[DATA/TAGS]_NAME <# of identifiers>, a, b, c, d, ... @@ -59,7 +64,13 @@ tgMeshDel, tgMeshMod, tgMeshReq, tgMeshClr, \ tgTexDel, tgTexMod, tgTexReq, tgTexClr - +struct TgtScene +{ + osp::TargetId cleanup; + osp::TargetId time; + osp::TargetId sync; + osp::TargetId resyncAll; +}; #define OSP_DATA_TESTAPP_MATERIAL 2, \ idMatEnts, idMatDirty @@ -216,6 +227,11 @@ #define OSP_TAGS_TESTAPP_APP 2, \ tgRenderEvt, tgInputEvt +struct TgtApplication +{ + osp::TargetId input; + osp::TargetId render; +}; #define OSP_DATA_TESTAPP_APP_MAGNUM 3, \ @@ -272,3 +288,5 @@ idVhControls #define OSP_TAGS_TESTAPP_VEHICLE_CONTROL 2, \ tgSelUsrCtrlMod, tgSelUsrCtrlReq + +} // namespace testapp::targets diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 8bbfa864..ca91e935 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -54,47 +54,47 @@ using namespace osp; namespace testapp { -static void setup_magnum_draw(MainView mainView, Session const& magnum, Session const& scnCommon, Session const& scnRender, std::vector runTagsVec = {}) +static void setup_magnum_draw(MainView mainView, Session const& application, Session const& scene, Session const& scnRender, std::vector run = {}) { - OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); - OSP_SESSION_UNPACK_TAGS(magnum, TESTAPP_APP_MAGNUM); + OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); + OSP_SESSION_UNPACK_DATA(application, TESTAPP_APP_MAGNUM); - Tags &rTags = mainView.m_rTags; Tasks &rTasks = mainView.m_rTasks; TopTaskDataVec_t &rTaskData = mainView.m_rTaskData; - ExecutionContext &rExec = mainView.m_rExec; + ExecContext &rExec = mainView.m_rExec; ArrayView topData = mainView.m_topData; auto &rActiveApp = top_get(topData, idActiveApp); auto &rCamera = top_get(topData, idCamera); rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); + auto tgScn = scene .get_targets(); + auto tgApp = application .get_targets(); + // Run Resync tasks to mark all used gpu resources as dirty - top_enqueue_quick(rTags, rTasks, rExec, {tgResyncEvt}); - top_run_blocking(rTags, rTasks, rTaskData, topData, rExec); + top_enqueue_quick(rTasks, rExec, {tgScn.resyncAll}); + top_run_blocking(rTasks, rTaskData, topData, rExec); - runTagsVec.insert(runTagsVec.end(), {tgSyncEvt, tgSceneEvt, tgTimeEvt, tgRenderEvt, tgInputEvt}); + run.insert(run.end(), {tgScn.sync, tgScn.time, tgApp.input, tgApp.render}); - // runTagsVec gets copied but who cares lol - rActiveApp.set_on_draw( [&rTags, &rTasks, &rExec, &rTaskData, topData, runTagsVec = std::move(runTagsVec)] + // run gets copied but who cares lol + rActiveApp.set_on_draw( [&rTasks, &rExec, &rTaskData, topData, runTagsVec = std::move(run)] (ActiveApplication& rApp, float delta) { // Magnum Application's main loop is here - top_enqueue_quick(rTags, rTasks, rExec, runTagsVec); - top_run_blocking(rTags, rTasks, rTaskData, topData, rExec); + top_enqueue_quick(rTasks, rExec, runTagsVec); + top_run_blocking(rTasks, rTaskData, topData, rExec); // Enqueued tasks that don't run indicate a deadlock - if ( ! std::all_of(std::begin(rExec.m_taskQueuedCounts), - std::end(rExec.m_taskQueuedCounts), - [] (unsigned int n) { return n == 0; })) - { - OSP_LOG_ERROR("Deadlock detected!"); - debug_top_print_deadlock(rTags, rTasks, rTaskData, rExec); - std::abort(); - } +// if ( ! std::all_of(std::begin(rExec.m_taskQueuedCounts), +// std::end(rExec.m_taskQueuedCounts), +// [] (unsigned int n) { return n == 0; })) +// { +// OSP_LOG_ERROR("Deadlock detected!"); +// debug_top_print_deadlock( rTasks, rTaskData, rExec); +// std::abort(); +// } }); } @@ -120,7 +120,7 @@ static ScenarioMap_t make_scenarios() return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, [[maybe_unused]] Sessions_t& rendererOut) { - TopDataId const idSceneData = scene.front().m_dataIds.front(); + TopDataId const idSceneData = scene.front().m_data.front(); auto& rScene = top_get(mainView.m_topData, idSceneData); OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); @@ -133,6 +133,8 @@ static ScenarioMap_t make_scenarios() }; }); +#if 0 + add_scenario("physics", "Newton Dynamics integration test scenario", [] (MainView mainView, Sessions_t& sceneOut) -> RendererSetup_t { @@ -335,6 +337,8 @@ static ScenarioMap_t make_scenarios() }; }); +#endif + return scenarioMap; } diff --git a/src/test_application/activescenes/scenarios.h b/src/test_application/activescenes/scenarios.h index 7f565795..da4282d6 100644 --- a/src/test_application/activescenes/scenarios.h +++ b/src/test_application/activescenes/scenarios.h @@ -29,10 +29,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -47,7 +47,6 @@ namespace testapp struct MainView { osp::ArrayView m_topData; - osp::Tags & m_rTags; osp::Tasks & m_rTasks; osp::ExecutionContext & m_rExec; osp::TopTaskDataVec_t & m_rTaskData; @@ -55,8 +54,6 @@ struct MainView osp::PkgId m_defaultPkg; }; -using Builder_t = osp::TaskBuilder; - using RendererSetup_t = void(*)(MainView, osp::Session const&, osp::Sessions_t const&, osp::Sessions_t&); using SceneSetup_t = RendererSetup_t(*)(MainView, osp::Sessions_t&); diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index 5dbe4cc7..d51c54f5 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -42,10 +42,11 @@ using namespace osp::active; namespace testapp::scenes { +#if 0 + Session setup_common_scene( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, TopDataId const idResources, PkgId const pkg) { @@ -53,8 +54,7 @@ Session setup_common_scene( Session scnCommon; OSP_SESSION_ACQUIRE_DATA(scnCommon, topData, TESTAPP_COMMON_SCENE); - OSP_SESSION_ACQUIRE_TAGS(scnCommon, rTags, TESTAPP_COMMON_SCENE); - scnCommon.m_tgCleanupEvt = tgCleanupEvt; + top_emplace< float > (topData, idDeltaTimeIn, 1.0f / 60.0f); top_emplace< EntVector_t > (topData, idDelEnts); @@ -67,36 +67,42 @@ Session setup_common_scene( auto &rDrawingRes = top_emplace< ACtxDrawingRes > (topData, idDrawingRes); auto &rNMesh = top_emplace< NamedMeshes > (topData, idNMesh); - rBuilder.tag(tgEntNew) .depend_on({tgEntDel}); - rBuilder.tag(tgEntReq) .depend_on({tgEntDel, tgEntNew}); - rBuilder.tag(tgDelEntReq) .depend_on({tgDelEntMod}); - rBuilder.tag(tgDelEntClr) .depend_on({tgDelEntMod, tgDelEntReq}); - rBuilder.tag(tgDelTotalReq) .depend_on({tgDelTotalMod}); - rBuilder.tag(tgDelTotalClr) .depend_on({tgDelTotalMod, tgDelTotalReq}); - rBuilder.tag(tgTransformDel) .depend_on({tgTransformMod}); - rBuilder.tag(tgTransformNew) .depend_on({tgTransformMod, tgTransformDel}); - rBuilder.tag(tgTransformReq) .depend_on({tgTransformMod, tgTransformDel, tgTransformNew}); - rBuilder.tag(tgHierNew) .depend_on({tgHierDel}); - rBuilder.tag(tgHierModEnd) .depend_on({tgHierDel, tgHierNew, tgHierMod}); - rBuilder.tag(tgHierReq) .depend_on({tgHierMod, tgHierModEnd}); - rBuilder.tag(tgDrawMod) .depend_on({tgDrawDel}); - rBuilder.tag(tgDrawReq) .depend_on({tgDrawDel, tgDrawMod}); - rBuilder.tag(tgDelDrawEntReq) .depend_on({tgDelDrawEntMod}); - rBuilder.tag(tgDelDrawEntClr) .depend_on({tgDelDrawEntMod, tgDelDrawEntReq}); - rBuilder.tag(tgMeshMod) .depend_on({tgMeshDel}); - rBuilder.tag(tgMeshReq) .depend_on({tgMeshDel, tgMeshMod}); - rBuilder.tag(tgMeshClr) .depend_on({tgMeshDel, tgMeshMod, tgMeshReq}); - rBuilder.tag(tgTexMod) .depend_on({tgTexDel}); - rBuilder.tag(tgTexReq) .depend_on({tgTexDel, tgTexMod}); - rBuilder.tag(tgTexClr) .depend_on({tgTexDel, tgTexMod, tgTexReq}); - - scnCommon.task() = rBuilder.task().assign({tgResyncEvt}).data( - "Set entity meshes and textures dirty", - TopDataIds_t{ idDrawing}, - wrap_args([] (ACtxDrawing& rDrawing) noexcept +// rBuilder.tag(tgEntNew) .depend_on({tgEntDel}); +// rBuilder.tag(tgEntReq) .depend_on({tgEntDel, tgEntNew}); +// rBuilder.tag(tgDelEntReq) .depend_on({tgDelEntMod}); +// rBuilder.tag(tgDelEntClr) .depend_on({tgDelEntMod, tgDelEntReq}); +// rBuilder.tag(tgDelTotalReq) .depend_on({tgDelTotalMod}); +// rBuilder.tag(tgDelTotalClr) .depend_on({tgDelTotalMod, tgDelTotalReq}); +// rBuilder.tag(tgTransformDel) .depend_on({tgTransformMod}); +// rBuilder.tag(tgTransformNew) .depend_on({tgTransformMod, tgTransformDel}); +// rBuilder.tag(tgTransformReq) .depend_on({tgTransformMod, tgTransformDel, tgTransformNew}); +// rBuilder.tag(tgHierNew) .depend_on({tgHierDel}); +// rBuilder.tag(tgHierModEnd) .depend_on({tgHierDel, tgHierNew, tgHierMod}); +// rBuilder.tag(tgHierReq) .depend_on({tgHierMod, tgHierModEnd}); +// rBuilder.tag(tgDrawMod) .depend_on({tgDrawDel}); +// rBuilder.tag(tgDrawReq) .depend_on({tgDrawDel, tgDrawMod}); +// rBuilder.tag(tgDelDrawEntReq) .depend_on({tgDelDrawEntMod}); +// rBuilder.tag(tgDelDrawEntClr) .depend_on({tgDelDrawEntMod, tgDelDrawEntReq}); +// rBuilder.tag(tgMeshMod) .depend_on({tgMeshDel}); +// rBuilder.tag(tgMeshReq) .depend_on({tgMeshDel, tgMeshMod}); +// rBuilder.tag(tgMeshClr) .depend_on({tgMeshDel, tgMeshMod, tgMeshReq}); +// rBuilder.tag(tgTexMod) .depend_on({tgTexDel}); +// rBuilder.tag(tgTexReq) .depend_on({tgTexDel, tgTexMod}); +// rBuilder.tag(tgTexClr) .depend_on({tgTexDel, tgTexMod, tgTexReq}); + + std::vector tasks; + + rBuilder.task() + .push_to(tasks) + .name("Set entity meshes and textures dirty") + .depends_on({}) + .data({ idDrawing}) + .func([] (ACtxDrawing& rDrawing) noexcept { SysRender::set_dirty_all(rDrawing); - })); + }); + + scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelEntReq, tgDelTotalMod}).data( "Create DeleteTotal vector, which includes descendents of deleted hierarchy entities", @@ -236,10 +242,10 @@ Session setup_common_scene( return scnCommon; } + Session setup_material( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& scnCommon) { OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); @@ -294,7 +300,9 @@ Session setup_material( return material; } -} +#endif + +} // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_common.h b/src/test_application/activescenes/scene_common.h index e75e85c5..9bddb0cf 100644 --- a/src/test_application/activescenes/scene_common.h +++ b/src/test_application/activescenes/scene_common.h @@ -60,9 +60,8 @@ struct NamedMeshes * @brief Support for Time, ActiveEnts, Hierarchy, Transforms, Drawing, and more... */ osp::Session setup_common_scene( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::TopDataId idResources, osp::PkgId pkg); @@ -72,9 +71,8 @@ osp::Session setup_common_scene( * Multiple material sessions can be setup for each material */ osp::Session setup_material( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon); } diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index b2ca709e..c55d595d 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -51,6 +51,8 @@ using namespace Magnum::Math::Literals; namespace testapp::scenes { +#if 0 + void add_floor( ArrayView const topData, Session const& scnCommon, @@ -129,9 +131,8 @@ void add_floor( } Session setup_camera_ctrl( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& app, Session const& scnRender) { @@ -161,9 +162,8 @@ Session setup_camera_ctrl( } Session setup_camera_free( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& app, Session const& scnCommon, Session const& camera) @@ -188,9 +188,8 @@ Session setup_camera_free( } Session setup_thrower( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& magnum, Session const& renderer, Session const& simpleCamera, @@ -236,9 +235,8 @@ Session setup_thrower( } Session setup_droppers( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& scnCommon, Session const& shapeSpawn) { @@ -296,4 +294,6 @@ Session setup_droppers( return droppers; } -} +#endif + +} // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_misc.h b/src/test_application/activescenes/scene_misc.h index 40901a61..054cf1c8 100644 --- a/src/test_application/activescenes/scene_misc.h +++ b/src/test_application/activescenes/scene_misc.h @@ -41,9 +41,8 @@ void add_floor( * @brief Create CameraController connected to an app's UserInputHandler */ osp::Session setup_camera_ctrl( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& app, osp::Session const& scnRender); @@ -51,9 +50,8 @@ osp::Session setup_camera_ctrl( * @brief Adds free cam controls to a CameraController */ osp::Session setup_camera_free( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& app, osp::Session const& scnCommon, osp::Session const& camera); @@ -62,9 +60,8 @@ osp::Session setup_camera_free( * @brief Throws spheres when pressing space */ osp::Session setup_thrower( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& magnum, osp::Session const& renderer, osp::Session const& simpleCamera, @@ -74,9 +71,8 @@ osp::Session setup_thrower( * @brief Spawn blocks every 2 seconds and spheres every 1 second */ osp::Session setup_droppers( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& shapeSpawn); diff --git a/src/test_application/activescenes/scene_newton.cpp b/src/test_application/activescenes/scene_newton.cpp index 2a4c4d2a..30d5027f 100644 --- a/src/test_application/activescenes/scene_newton.cpp +++ b/src/test_application/activescenes/scene_newton.cpp @@ -56,9 +56,10 @@ using Corrade::Containers::arrayView; namespace testapp::scenes { +#if 0 Session setup_newton( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, Session const& scnCommon, @@ -104,7 +105,7 @@ Session setup_newton( } osp::Session setup_newton_factors( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView topData, Tags& rTags) { @@ -119,7 +120,7 @@ osp::Session setup_newton_factors( } osp::Session setup_newton_force_accel( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView topData, Tags& rTags, Session const& newton, @@ -164,7 +165,7 @@ osp::Session setup_newton_force_accel( Session setup_shape_spawn_newton( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, Session const& scnCommon, @@ -272,7 +273,7 @@ void compound_collect_recurse( } Session setup_vehicle_spawn_newton( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, Session const& scnCommon, @@ -580,7 +581,7 @@ static void assign_rockets( } Session setup_rocket_thrust_newton( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, Session const& scnCommon, @@ -697,6 +698,7 @@ Session setup_rocket_thrust_newton( return rocketNwt; } +#endif } // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_newton.h b/src/test_application/activescenes/scene_newton.h index 5808565f..53bd4395 100644 --- a/src/test_application/activescenes/scene_newton.h +++ b/src/test_application/activescenes/scene_newton.h @@ -44,9 +44,8 @@ namespace testapp::scenes * @brief Newton Dynamics physics integration */ osp::Session setup_newton( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& physics); @@ -57,17 +56,15 @@ osp::Session setup_newton( * functions contribute to its force and torque */ osp::Session setup_newton_factors( - Builder_t& rBuilder, - osp::ArrayView topData, - osp::Tags& rTags); + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData); /** * @brief Setup constant acceleration force, add to a force factor bitset */ osp::Session setup_newton_force_accel( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& newton, osp::Session const& nwtFactors, osp::Vector3 accel); @@ -76,9 +73,8 @@ osp::Session setup_newton_force_accel( * @brief Support for Shape Spawner physics using Newton Dynamics */ osp::Session setup_shape_spawn_newton( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& physics, osp::Session const& shapeSpawn, @@ -89,9 +85,8 @@ osp::Session setup_shape_spawn_newton( * @brief Support for Vehicle physics using Newton Dynamics */ osp::Session setup_vehicle_spawn_newton( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& physics, osp::Session const& prefabs, @@ -104,9 +99,8 @@ osp::Session setup_vehicle_spawn_newton( * @brief Add thrust forces to Magic Rockets from setup_mach_rocket */ osp::Session setup_rocket_thrust_newton( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& physics, osp::Session const& prefabs, diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index d9e090a3..3ecfe4fc 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -48,8 +48,10 @@ using Corrade::Containers::arrayView; namespace testapp::scenes { +#if 0 + Session setup_physics( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, Session const& scnCommon) @@ -87,7 +89,7 @@ Session setup_physics( } Session setup_shape_spawn( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, Session const& scnCommon, @@ -236,7 +238,7 @@ Session setup_shape_spawn( Session setup_prefabs( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, Session const& scnCommon, @@ -364,7 +366,7 @@ Session setup_prefabs( } Session setup_bounds( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, Session const& scnCommon, @@ -445,6 +447,7 @@ Session setup_bounds( return bounds; } +#endif } // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_physics.h b/src/test_application/activescenes/scene_physics.h index b131e9f3..0b9f29f7 100644 --- a/src/test_application/activescenes/scene_physics.h +++ b/src/test_application/activescenes/scene_physics.h @@ -53,18 +53,16 @@ using SpawnerVec_t = std::vector; * Independent of whichever physics engine is used */ osp::Session setup_physics( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon); /** * @brief Newton Dynamics physics integration */ osp::Session setup_newton_physics( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& physics); @@ -72,9 +70,8 @@ osp::Session setup_newton_physics( * @brief Queues and logic for spawning physics shapes */ osp::Session setup_shape_spawn( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& physics, osp::Session const& material); @@ -83,9 +80,8 @@ osp::Session setup_shape_spawn( * @brief Queues and logic for spawning Prefab resources */ osp::Session setup_prefabs( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& physics, osp::Session const& material, @@ -95,9 +91,8 @@ osp::Session setup_prefabs( * @brief Entity set to delete entities under Z = -10, added to spawned shapes */ osp::Session setup_bounds( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& physics, osp::Session const& shapeSpawn); diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 768f02da..8d679877 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -62,10 +62,11 @@ using Magnum::GL::Mesh; namespace testapp::scenes { +#if 0 + Session setup_magnum_application( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, TopDataId const idResources, ActiveApplication::Arguments args) { @@ -96,9 +97,8 @@ Session setup_magnum_application( Session setup_scene_renderer( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& magnum, Session const& scnCommon, TopDataId const idResources) @@ -249,9 +249,8 @@ Session setup_scene_renderer( } Session setup_shader_visualizer( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& magnum, Session const& scnCommon, Session const& scnRender, @@ -305,9 +304,8 @@ Session setup_shader_visualizer( Session setup_shader_flat( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& magnum, Session const& scnCommon, Session const& scnRender, @@ -351,9 +349,8 @@ Session setup_shader_flat( Session setup_shader_phong( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& magnum, Session const& scnCommon, Session const& scnRender, @@ -405,9 +402,8 @@ struct IndicatorMesh Session setup_thrust_indicators( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& magnum, Session const& scnCommon, Session const& parts, @@ -556,9 +552,8 @@ Session setup_thrust_indicators( Session setup_cursor( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& magnum, Session const& scnCommon, Session const& scnRender, @@ -623,9 +618,8 @@ Session setup_cursor( Session setup_uni_test_planets_renderer( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& magnum, Session const& scnRender, Session const& scnCommon, @@ -774,4 +768,6 @@ Session setup_uni_test_planets_renderer( return uniTestPlanetsRdr; } +#endif + } // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index 47151b0d..ecec7833 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -34,9 +34,8 @@ namespace testapp::scenes osp::Session setup_magnum_application( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::TopDataId const idResources, ActiveApplication::Arguments args); @@ -44,9 +43,8 @@ osp::Session setup_magnum_application( * @brief Magnum-powered OpenGL Renderer */ osp::Session setup_scene_renderer( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& magnum, osp::Session const& scene, osp::TopDataId idResources); @@ -55,9 +53,8 @@ osp::Session setup_scene_renderer( * @brief Magnum MeshVisualizer shader and optional material for drawing ActiveEnts with it */ osp::Session setup_shader_visualizer( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& magnum, osp::Session const& scene, osp::Session const& scnRender, @@ -67,9 +64,8 @@ osp::Session setup_shader_visualizer( * @brief Magnum Flat shader and optional material for drawing ActiveEnts with it */ osp::Session setup_shader_flat( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& magnum, osp::Session const& scene, osp::Session const& scnRender, @@ -79,9 +75,8 @@ osp::Session setup_shader_flat( * @brief Magnum Phong shader and optional material for drawing ActiveEnts with it */ osp::Session setup_shader_phong( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& magnum, osp::Session const& scene, osp::Session const& scnRender, @@ -91,9 +86,8 @@ osp::Session setup_shader_phong( * @brief Red indicators over Magic Rockets */ osp::Session setup_thrust_indicators( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& magnum, osp::Session const& scnCommon, osp::Session const& parts, @@ -108,9 +102,8 @@ osp::Session setup_thrust_indicators( * @brief Wireframe cube over the camera controller's target */ osp::Session setup_cursor( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& magnum, osp::Session const& scnCommon, osp::Session const& scnRender, @@ -123,9 +116,8 @@ osp::Session setup_cursor( * @brief Draw universe, specifically designed for setup_uni_test_planets */ osp::Session setup_uni_test_planets_renderer( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& magnum, osp::Session const& scnRender, osp::Session const& scnCommon, diff --git a/src/test_application/activescenes/scene_universe.cpp b/src/test_application/activescenes/scene_universe.cpp index 9f4d2a53..d859f92c 100644 --- a/src/test_application/activescenes/scene_universe.cpp +++ b/src/test_application/activescenes/scene_universe.cpp @@ -41,10 +41,11 @@ using namespace osp::universe; namespace testapp::scenes { +#if 0 + Session setup_uni_core( - Builder_t& rBuilder, - ArrayView topData, - Tags& rTags) + TopTaskBuilder& rBuilder, + ArrayView topData) { Session uniCore; OSP_SESSION_ACQUIRE_DATA(uniCore, topData, TESTAPP_UNI_CORE); @@ -57,9 +58,8 @@ Session setup_uni_core( Session setup_uni_sceneframe( - Builder_t& rBuilder, - ArrayView topData, - Tags& rTags) + TopTaskBuilder& rBuilder, + ArrayView topData) { Session scnFrame; OSP_SESSION_ACQUIRE_DATA(scnFrame, topData, TESTAPP_UNI_SCENEFRAME); @@ -74,9 +74,8 @@ Session setup_uni_sceneframe( Session setup_uni_test_planets( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView topData, - Tags& rTags, Session const& uniCore, Session const& scnFrame) { @@ -289,4 +288,6 @@ Session setup_uni_test_planets( return uniTestPlanets; } +#endif + } // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_universe.h b/src/test_application/activescenes/scene_universe.h index 9cafb953..4b912c40 100644 --- a/src/test_application/activescenes/scene_universe.h +++ b/src/test_application/activescenes/scene_universe.h @@ -33,25 +33,22 @@ namespace testapp::scenes * @brief Core Universe struct with addressable Coordinate Spaces */ osp::Session setup_uni_core( - Builder_t& rBuilder, - osp::ArrayView topData, - osp::Tags& rTags); + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData); /** * @brief Represents the physics scene's presence in a Universe */ osp::Session setup_uni_sceneframe( - Builder_t& rBuilder, - osp::ArrayView topData, - osp::Tags& rTags); + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData); /** * @brief Unrealistic planets test, allows SceneFrame to move around and get captured into planets */ osp::Session setup_uni_test_planets( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& uniCore, osp::Session const& uniScnFrame); diff --git a/src/test_application/activescenes/scene_vehicles.cpp b/src/test_application/activescenes/scene_vehicles.cpp index 2497b238..40f2ca42 100644 --- a/src/test_application/activescenes/scene_vehicles.cpp +++ b/src/test_application/activescenes/scene_vehicles.cpp @@ -51,11 +51,11 @@ using namespace Magnum::Math::Literals; namespace testapp::scenes { +#if 0 Session setup_parts( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& scnCommon, TopDataId const idResources) { @@ -141,9 +141,8 @@ Session setup_parts( } Session setup_signals_float( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& scnCommon, Session const& parts) { @@ -238,9 +237,8 @@ TopTaskFunc_t gen_allocate_mach_bitsets() } Session setup_mach_rocket( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& scnCommon, Session const& parts, Session const& signalsFloat) @@ -267,9 +265,8 @@ Session setup_mach_rocket( } Session setup_mach_rcsdriver( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& scnCommon, Session const& parts, Session const& signalsFloat) @@ -357,9 +354,8 @@ Session setup_mach_rcsdriver( } Session setup_vehicle_spawn( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& scnCommon) { OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); @@ -389,9 +385,8 @@ Session setup_vehicle_spawn( } Session setup_vehicle_spawn_vb( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - [[maybe_unused]] Tags& rTags, Session const& scnCommon, Session const& prefabs, Session const& parts, @@ -961,9 +956,8 @@ void add_rcs_block(VehicleBuilder& rBuilder, VehicleBuilder::WeldVec_t& rWeldTo, } Session setup_test_vehicles( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - [[maybe_unused]] Tags& rTags, Session const& scnCommon, TopDataId const idResources) { @@ -1078,9 +1072,8 @@ struct VehicleTestControls }; Session setup_vehicle_control( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, Session const& scnCommon, Session const& parts, Session const& signalsFloat, @@ -1211,9 +1204,8 @@ Session setup_vehicle_control( } Session setup_camera_vehicle( - Builder_t& rBuilder, + TopTaskBuilder& rBuilder, [[maybe_unused]] ArrayView const topData, - [[maybe_unused]] Tags& rTags, Session const& app, Session const& scnCommon, Session const& parts, @@ -1269,4 +1261,6 @@ Session setup_camera_vehicle( return cameraFree; } +#endif + } // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_vehicles.h b/src/test_application/activescenes/scene_vehicles.h index f24063e8..987a7a52 100644 --- a/src/test_application/activescenes/scene_vehicles.h +++ b/src/test_application/activescenes/scene_vehicles.h @@ -31,15 +31,12 @@ namespace testapp { struct VehicleData; } namespace testapp::scenes { -using MachTypeToEvt_t = std::vector; - /** * @brief Support for Parts, Machines, and Links */ osp::Session setup_parts( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::TopDataId const idResources); @@ -73,9 +70,8 @@ osp::Session setup_parts( * another float signal, all running within a single frame. */ osp::Session setup_signals_float( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& parts); @@ -85,9 +81,8 @@ osp::Session setup_signals_float( * This only sets up links and does not apply forces, see setup_rocket_thrust_newton */ osp::Session setup_mach_rocket( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& parts, osp::Session const& signalsFloat); @@ -96,9 +91,8 @@ osp::Session setup_mach_rocket( * @brief Links for RCS Drivers, which output thrust levels given pitch/yaw/roll controls */ osp::Session setup_mach_rcsdriver( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& parts, osp::Session const& signalsFloat); @@ -110,18 +104,16 @@ osp::Session setup_mach_rcsdriver( * of conencted Parts */ osp::Session setup_vehicle_spawn( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon); /** * @brief Support VehicleBuilder data to be used to spawn vehicles */ osp::Session setup_vehicle_spawn_vb( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& prefabs, osp::Session const& parts, @@ -133,9 +125,8 @@ osp::Session setup_vehicle_spawn_vb( * @brief Build "Test Vehicle" data, so they can be spawned */ osp::Session setup_test_vehicles( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::TopDataId const idResources); @@ -143,9 +134,8 @@ osp::Session setup_test_vehicles( * @brief Controls to select and control a UserControl Machine */ osp::Session setup_vehicle_control( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& scnCommon, osp::Session const& parts, osp::Session const& signalsFloat, @@ -155,9 +145,8 @@ osp::Session setup_vehicle_control( * @brief Camera which can free cam or follow a selected vehicle */ osp::Session setup_camera_vehicle( - Builder_t& rBuilder, + osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Tags& rTags, osp::Session const& app, osp::Session const& scnCommon, osp::Session const& parts, diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index 095f64ff..d2adcc7b 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -110,11 +110,10 @@ std::vector g_appTopData; // Each TopTask is given a vector of TopDataIds its allowed to access osp::Tasks g_tasks; osp::TopTaskDataVec_t g_taskData; -osp::Tags g_tags; // Current execution state of TopTasks // g_tasks, g_taskData, and g_tags stay constant during execution -osp::ExecutionContext g_exec; +osp::ExecContext g_exec; // Sessions bundle together and own TopDataIds, TopTaskIds, and TagsIds // Sessions intend to add support for something to exist in the world @@ -151,20 +150,19 @@ char** g_argv; static void close_sessions(osp::Sessions_t &rSessions) { - osp::top_close_session(g_tags, g_tasks, g_taskData, g_appTopData, g_exec, rSessions); + osp::top_close_session(g_tasks, g_taskData, g_appTopData, g_exec, rSessions); rSessions.clear(); } static void close_session(osp::Session &rSession) { - osp::top_close_session(g_tags, g_tasks, g_taskData, g_appTopData, g_exec, osp::ArrayView(&rSession, 1)); + osp::top_close_session(g_tasks, g_taskData, g_appTopData, g_exec, osp::ArrayView(&rSession, 1)); } static MainView get_main_view() { return { .m_topData = g_appTopData, - .m_rTags = g_tags, .m_rTasks = g_tasks, .m_rExec = g_exec, .m_rTaskData = g_taskData, @@ -245,7 +243,7 @@ int debug_cli_loop() std::cout << "> "; std::cin >> command; - bool magnumOpen = ! g_magnum.m_dataIds.empty(); + bool magnumOpen = ! g_magnum.m_data.empty(); if (auto const it = scenarios().find(command); it != std::end(scenarios())) { @@ -324,8 +322,8 @@ void start_magnum_async() osp::set_thread_logger(g_logMagnumApp); // Start Magnum application session - Builder_t builder{g_tags, g_tasks, g_taskData}; - g_magnum = scenes::setup_magnum_application(builder, g_appTopData, g_tags, g_idResources, {g_argc, g_argv}); + osp::TopTaskBuilder builder{g_taskData}; + g_magnum = scenes::setup_magnum_application(builder, g_appTopData, g_idResources, {g_argc, g_argv}); OSP_SESSION_UNPACK_DATA(g_magnum, TESTAPP_APP_MAGNUM); // declares idActiveApp auto &rActiveApp = osp::top_get(g_appTopData, idActiveApp); diff --git a/test/tasks/CMakeLists.txt b/test/tasks/CMakeLists.txt index d9ae2fdf..aa4d056a 100644 --- a/test/tasks/CMakeLists.txt +++ b/test/tasks/CMakeLists.txt @@ -26,4 +26,4 @@ PROJECT(test_tasks CXX) ADD_TEST_DIRECTORY(${PROJECT_NAME}) TARGET_LINK_LIBRARIES(test_tasks PRIVATE longeron EnTT::EnTT Magnum::Magnum) -TARGET_SOURCES(test_tasks PRIVATE "${CMAKE_SOURCE_DIR}/src/osp/tasks/builder.cpp" "${CMAKE_SOURCE_DIR}/src/osp/tasks/execute.cpp") +TARGET_SOURCES(test_tasks PRIVATE "${CMAKE_SOURCE_DIR}/src/osp/tasks/tasks.cpp" "${CMAKE_SOURCE_DIR}/src/osp/tasks/execute.cpp") diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index b458c792..f7af7e9b 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -33,7 +33,6 @@ #include using namespace osp; -using namespace osp::tasks; template bool contains(RANGE_T const& range, VALUE_T const& value) noexcept @@ -49,7 +48,7 @@ bool contains(RANGE_T const& range, VALUE_T const& value) noexcept } template -void randomized_singlethreaded_execute(Tasks const& tasks, ExecutionContext& rExec, std::mt19937 &rRand, int maxRuns, RUN_TASK_T && runTask) +void randomized_singlethreaded_execute(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, std::mt19937 &rRand, int maxRuns, RUN_TASK_T && runTask) { std::size_t tasksLeft = rExec.m_tasksQueued.count(); @@ -61,11 +60,11 @@ void randomized_singlethreaded_execute(Tasks const& tasks, ExecutionContext& rEx } // This solution of "pick random '1' bit in a bit vector" is very inefficient - std::size_t const randomTask = *std::next(rExec.m_tasksQueued.ones().begin(), rRand() % tasksLeft); + auto const randomTask = TaskId(*std::next(rExec.m_tasksQueued.ones().begin(), rRand() % tasksLeft)); - FulfillDirty_t const status = runTask(TaskId(randomTask)); - mark_completed_task(tasks, rExec, TaskId(randomTask), status); - int const newTasks = enqueue_dirty(tasks, rExec); + FulfillDirty_t const status = runTask(randomTask); + mark_completed_task(tasks, graph, rExec, randomTask, status); + int const newTasks = enqueue_dirty(tasks, graph, rExec); tasksLeft = tasksLeft - 1 + newTasks; } @@ -91,8 +90,10 @@ TEST(Tasks, BasicParallelSingleThreaded) constexpr int sc_totalTaskCount = sc_pusherTaskCount + 1; std::mt19937 randGen(69); - TaskFuncVec_t functions; - auto builder = Builder_t{functions}; + Tasks tasks; + TaskEdges edges; + TaskFuncVec_t functions; + Builder_t builder{tasks, edges, functions}; auto const [ inputIn, /// Input int, manually set dirty when it changes @@ -124,8 +125,8 @@ TEST(Tasks, BasicParallelSingleThreaded) }); } - Tasks const tasks = finalize(std::move(builder)); - ExecutionContext exec; + ExecGraph const graph = make_exec_graph(tasks, {&edges}); + ExecContext exec; exec.resize(tasks); @@ -140,9 +141,9 @@ TEST(Tasks, BasicParallelSingleThreaded) // Enqueue initial tasks // This roughly indicates "Time has changed" and "Render requested" exec.m_targetDirty.set(std::size_t(inputIn)); - enqueue_dirty(tasks, exec); + enqueue_dirty(tasks, graph, exec); - randomized_singlethreaded_execute(tasks, exec, randGen, sc_totalTaskCount, [&functions, &input, &output] (TaskId const task) -> FulfillDirty_t + randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output] (TaskId const task) -> FulfillDirty_t { return functions[task](input, output); }); @@ -182,8 +183,10 @@ TEST(Tasks, BasicSingleThreaded) constexpr int sc_repetitions = 32; std::mt19937 randGen(69); - TaskFuncVec_t functions; - auto builder = Builder_t{functions}; + Tasks tasks; + TaskEdges edges; + TaskFuncVec_t functions; + Builder_t builder{tasks, edges, functions}; auto const tgt = builder.create_targets(); // Start adding tasks. The order these are added does not matter. @@ -241,27 +244,27 @@ TEST(Tasks, BasicSingleThreaded) return {{0b01}}; }); - Tasks const tasks = finalize(std::move(builder)); + ExecGraph const graph = make_exec_graph(tasks, {&edges}); // Random checks to assure the graph structure is properly built - ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.timeIn) ].size(), 3); - ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.forces) ].size(), 1); - ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.positions) ].size(), 1); - ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.renderRequestIn) ].size(), 2); - ASSERT_EQ(tasks.m_targetDependents[ uint32_t(tgt.renderDoneOut) ].size(), 0); - ASSERT_TRUE( contains(tasks.m_targetFulfilledBy[uint32_t(tgt.forces)], taskB) ); - ASSERT_FALSE( contains(tasks.m_targetFulfilledBy[uint32_t(tgt.renderDoneOut)], taskC) ); - ASSERT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskA)], tgt.timeIn) ); - ASSERT_FALSE( contains(tasks.m_taskDependOn[uint32_t(taskB)], tgt.positions) ); - ASSERT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskC)], tgt.timeIn) ); - ASSERT_TRUE( contains(tasks.m_taskDependOn[uint32_t(taskC)], tgt.forces) ); - ASSERT_TRUE( contains(tasks.m_taskFulfill[uint32_t(taskD)], tgt.renderDoneOut) ); - ASSERT_FALSE( contains(tasks.m_taskFulfill[uint32_t(taskE)], tgt.forces) ); + ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.timeIn) ].size(), 3); + ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.forces) ].size(), 1); + ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.positions) ].size(), 1); + ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.renderRequestIn) ].size(), 2); + ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.renderDoneOut) ].size(), 0); + ASSERT_TRUE( contains(graph.m_targetFulfilledBy[uint32_t(tgt.forces)], taskB) ); + ASSERT_FALSE( contains(graph.m_targetFulfilledBy[uint32_t(tgt.renderDoneOut)], taskC) ); + ASSERT_TRUE( contains(graph.m_taskDependOn[uint32_t(taskA)], tgt.timeIn) ); + ASSERT_FALSE( contains(graph.m_taskDependOn[uint32_t(taskB)], tgt.positions) ); + ASSERT_TRUE( contains(graph.m_taskDependOn[uint32_t(taskC)], tgt.timeIn) ); + ASSERT_TRUE( contains(graph.m_taskDependOn[uint32_t(taskC)], tgt.forces) ); + ASSERT_TRUE( contains(graph.m_taskFulfill[uint32_t(taskD)], tgt.renderDoneOut) ); + ASSERT_FALSE( contains(graph.m_taskFulfill[uint32_t(taskE)], tgt.forces) ); // Execute TestWorld world; - ExecutionContext exec; + ExecContext exec; exec.resize(tasks); // Repeat (with randomness) to test many possible execution orders @@ -275,9 +278,11 @@ TEST(Tasks, BasicSingleThreaded) // This roughly indicates "Time has changed" and "Render requested" exec.m_targetDirty.set(std::size_t(tgt.timeIn)); exec.m_targetDirty.set(std::size_t(tgt.renderRequestIn)); - enqueue_dirty(tasks, exec); + enqueue_dirty(tasks, graph, exec); - randomized_singlethreaded_execute(tasks, exec, randGen, 5, [&functions, &world] (TaskId const task) -> FulfillDirty_t + randomized_singlethreaded_execute( + tasks, graph, exec, randGen, 5, + [&functions, &world] (TaskId const task) -> FulfillDirty_t { return functions[task](world); }); From 56125085f01821e5dcac9bb247186db25762ed4c Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Thu, 27 Apr 2023 12:16:45 -0700 Subject: [PATCH 13/35] Restructure testapp for new task changes, Fixes enginetest --- src/osp/tasks/execute.h | 7 +- src/osp/tasks/top_execute.cpp | 9 +- src/osp/tasks/top_session.cpp | 63 ++---- src/osp/tasks/top_session.h | 57 +++-- src/osp/tasks/top_utils.h | 44 ++-- .../activescenes/identifiers.h | 71 +++--- .../activescenes/scenarios.cpp | 64 +++--- src/test_application/activescenes/scenarios.h | 30 +-- .../activescenes/scenarios_enginetest.cpp | 2 + .../activescenes/scenarios_enginetest.h | 2 + .../activescenes/scene_renderer.cpp | 51 +++-- .../activescenes/scene_renderer.h | 8 +- src/test_application/activescenes/testapp.cpp | 0 src/test_application/activescenes/testapp.h | 0 src/test_application/main.cpp | 202 ++++-------------- src/test_application/testapp.cpp | 115 ++++++++++ src/test_application/testapp.h | 93 ++++++++ test/tasks/main.cpp | 3 +- 18 files changed, 447 insertions(+), 374 deletions(-) create mode 100644 src/test_application/activescenes/testapp.cpp create mode 100644 src/test_application/activescenes/testapp.h create mode 100644 src/test_application/testapp.cpp create mode 100644 src/test_application/testapp.h diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 44382bc9..234b8006 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -59,15 +59,14 @@ struct ExecContext // TODO: Consider multithreading. something something work stealing... // * Allow multiple threads to search for and execute tasks. Atomic access - // for ExecutionContext? Might be messy to implement. + // for ExecContext? Might be messy to implement. // * Only allow one thread to search for tasks, assign tasks to other // threads if they're available before running own task. Another thread // can take over once it completes its task. May be faster as only one - // thread is modifying ExecutionContext, and easier to implement. + // thread is modifying ExecContext, and easier to implement. // * Plug into an existing work queue library? -}; // struct ExecutionContext - +}; // struct ExecContext int enqueue_dirty(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec) noexcept; diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index de03707a..8ef14259 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -39,7 +39,7 @@ namespace osp { -void top_run_blocking(Tasks const& tasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec) +void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec) { std::vector topDataRefs; @@ -74,19 +74,18 @@ void top_run_blocking(Tasks const& tasks, TopTaskDataVec_t& rTaskData, ArrayView // Task actually runs here. Results are not yet used for anything. FulfillDirty_t const status = rTopTask.m_func(WorkerContext{}, topDataRefs); - mark_completed_task(tasks, rExec, task, status); + mark_completed_task(tasks, graph, rExec, task, status); } } -void top_enqueue_quick(Tasks const& tasks, ExecContext& rExec, ArrayView enqueue) +void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, ArrayView enqueue) { for (TargetId const target : enqueue) { rExec.m_targetDirty.set(std::size_t(target)); } - enqueue_dirty(tasks, rExec); + enqueue_dirty(tasks, graph, rExec); } - } // namespace testapp diff --git a/src/osp/tasks/top_session.cpp b/src/osp/tasks/top_session.cpp index e64af6db..1c535f84 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/osp/tasks/top_session.cpp @@ -33,38 +33,33 @@ #include -using Corrade::Containers::ArrayView; -using Corrade::Containers::StridedArrayView2D; -using Corrade::Containers::arrayView; - namespace osp { -void top_close_session(Tasks& rTasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, ArrayView sessions) +void top_close_session( + Tasks & rTasks, + ExecGraph const& graph, + TopTaskDataVec_t& rTaskData, + ArrayView topData, + ExecContext& rExec, + ArrayView sessions) { -#if 0 // Run cleanup tasks + for (Session &rSession : sessions) { - // Accumulate together all cleanup tags from all sessons - std::vector tagsToRun(rTags.m_tags.vec().size()); - auto tagsToRunBits = lgrn::bit_view(tagsToRun); - for (Session &rSession : sessions) + if (TargetId const cleanup = std::exchange(rSession.m_cleanupTgt, lgrn::id_null()); + cleanup != lgrn::id_null()) { - if (TagId const tgCleanupEvt = std::exchange(rSession.m_tgCleanupEvt, lgrn::id_null()); - tgCleanupEvt != lgrn::id_null()) - { - tagsToRunBits.set(std::size_t(tgCleanupEvt)); - } + rExec.m_targetDirty.set(std::size_t(cleanup)); } - - task_enqueue(rTags, rTasks, rExec, tagsToRun); - top_run_blocking(rTags, rTasks, rTaskData, topData, rExec); } + enqueue_dirty(rTasks, graph, rExec); + top_run_blocking(rTasks, graph, rTaskData, topData, rExec); // Clear each session's TopData for (Session &rSession : sessions) { - for (TopDataId const id : std::exchange(rSession.m_dataIds, {})) + for (TopDataId const id : std::exchange(rSession.m_data, {})) { if (id != lgrn::id_null()) { @@ -73,41 +68,19 @@ void top_close_session(Tasks& rTasks, TopTaskDataVec_t& rTaskData, ArrayView()) - { - rTags.m_tags.remove(tag); - - auto tagDepends = depends2d[std::size_t(tag)].asContiguous(); - std::fill(std::begin(tagDepends), std::end(tagDepends), lgrn::id_null()); - } - } - } -#endif } } // namespace testapp diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index aa7f876c..bf1db8cb 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -28,6 +28,7 @@ #include "execute.h" #include "tasks.h" #include "top_tasks.h" +#include "top_utils.h" #include @@ -39,13 +40,27 @@ #include -#define OSP_STRUCT_BIND_C(outerFunc, inner, count, ...) auto const [__VA_ARGS__] = outerFunc(inner) -#define OSP_STRUCT_BIND_B(x) x -#define OSP_STRUCT_BIND_A(...) OSP_STRUCT_BIND_B(OSP_STRUCT_BIND_C(__VA_ARGS__)) +#define OSP_AUX_DCDI_C(session, topData, count, ...) \ + session.m_data.resize(count); \ + top_reserve(topData, 0, session.m_data.begin(), session.m_data.end()); \ + auto const [__VA_ARGS__] = osp::unpack(session.m_data) +#define OSP_AUX_DCDI_B(x) x +#define OSP_AUX_DCDI_A(...) OSP_AUX_DCDI_B(OSP_AUX_DCDI_C(__VA_ARGS__)) + +/** + * @brief + */ +#define OSP_DECLARE_CREATE_DATA_IDS(session, topData, arglist) OSP_AUX_DCDI_A(session, topData, arglist); + + +#define OSP_AUX_DGDI_C(session, count, ...) \ + auto const [__VA_ARGS__] = osp::unpack(session.m_data) +#define OSP_AUX_DGDI_B(x) x +#define OSP_AUX_DGDI_A(...) OSP_AUX_DGDI_B(OSP_AUX_DGDI_C(__VA_ARGS__)) + +#define OSP_DECLARE_GET_DATA_IDS(session, arglist) OSP_AUX_DGDI_A(session, arglist); -#define OSP_SESSION_UNPACK_DATA(session, name) OSP_STRUCT_BIND_A(osp::unpack, (session).m_data, OSP_DATA_##name); -#define OSP_SESSION_ACQUIRE_DATA(session, topData, name) OSP_STRUCT_BIND_A((session).acquire_data, (topData), OSP_DATA_##name); namespace osp { @@ -68,8 +83,8 @@ struct Session return out; } - template - TGT_STRUCT_T create_targets(TaskBuilderData &rBuilder) + template + TGT_STRUCT_T create_targets(BUILDER_T &rBuilder) { static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); @@ -80,7 +95,7 @@ struct Session m_targets.resize(count); - rBuilder.m_targetIds.create(m_targets.begin(), m_targets.end()); + rBuilder.m_rTasks.m_targetIds.create(m_targets.begin(), m_targets.end()); return reinterpret_cast(*m_targets.data()); } @@ -92,10 +107,13 @@ struct Session constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); std::type_info const& info = typeid(TGT_STRUCT_T); - LGRN_ASSERTMV(m_targetStructHash == info.hash_code(), + LGRN_ASSERTMV(m_targetStructHash == info.hash_code() && count == m_targets.size(), "get_targets must use the same struct given to create_targets", m_targetStructHash, m_targetStructName, - info.hash_code(), info.name()); + info.hash_code(), info.name(), + m_targets.size()); + + return reinterpret_cast(*m_targets.data()); } TaskId& task() @@ -107,29 +125,22 @@ struct Session std::vector m_targets; std::vector m_tasks; - TargetId m_tgCleanupEvt{lgrn::id_null()}; + TargetId m_cleanupTgt{lgrn::id_null()}; std::size_t m_targetStructHash{0}; std::string m_targetStructName; }; -template -std::vector to_target_vec(TGT_STRUCT_T const& targets) +struct SessionGroup { - static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); - constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); - - TargetId const* pData = reinterpret_cast(std::addressof(targets)); - - return {pData, pData + count}; -} - -using Sessions_t = std::vector; + std::vector m_sessions; + TaskEdges m_edges; +}; /** * @brief Close sessions, delete all their associated TopData, Tasks, and Targets. */ -void top_close_session(Tasks& rTasks, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecutionContext& rExec, ArrayView sessions); +void top_close_session(Tasks& rTasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, ArrayView sessions); } // namespace osp diff --git a/src/osp/tasks/top_utils.h b/src/osp/tasks/top_utils.h index a9bad063..2345affb 100644 --- a/src/osp/tasks/top_utils.h +++ b/src/osp/tasks/top_utils.h @@ -179,25 +179,16 @@ constexpr TopTaskFunc_t wrap_args(FUNC_T funcArg) //----------------------------------------------------------------------------- -struct TopTaskBuilder; - -struct TopTaskRef : public TaskRefBase -{ - TopTaskRef& name(std::string_view debugName); - TopTaskRef& data(std::initializer_list dataUsed); - - template - TopTaskRef& func(FUNC_T funcArg); - TopTaskRef& func_raw(TopTaskFunc_t func); - - template - TopTaskRef& push_to(CONTAINER_T& rContainer); -}; +struct TopTaskRef; +/** + * @brief Convenient interface for building TopTasks + */ struct TopTaskBuilder : public TaskBuilderBase { - TopTaskBuilder(TopTaskDataVec_t& data) - : m_rData{data} + TopTaskBuilder(Tasks& rTasks, TaskEdges& rEdges, TopTaskDataVec_t& rData) + : TaskBuilderBase( {rTasks, rEdges} ) + , m_rData{rData} { } TopTaskBuilder(TopTaskBuilder const& copy) = delete; TopTaskBuilder(TopTaskBuilder && move) = default; @@ -207,16 +198,29 @@ struct TopTaskBuilder : public TaskBuilderBase TopTaskDataVec_t & m_rData; }; +struct TopTaskRef : public TaskRefBase +{ + inline TopTaskRef& name(std::string_view debugName); + inline TopTaskRef& data(std::initializer_list dataUsed); + + template + TopTaskRef& func(FUNC_T funcArg); + inline TopTaskRef& func_raw(TopTaskFunc_t func); + + template + TopTaskRef& push_to(CONTAINER_T& rContainer); +}; + TopTaskRef& TopTaskRef::name(std::string_view debugName) { - m_rBuilder.m_rData.resize(m_rBuilder.m_taskIds.capacity()); + m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_debugName = debugName; return *this; } TopTaskRef& TopTaskRef::data(std::initializer_list dataUsed) { - m_rBuilder.m_rData.resize(m_rBuilder.m_taskIds.capacity()); + m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_dataUsed = dataUsed; return *this; } @@ -224,14 +228,14 @@ TopTaskRef& TopTaskRef::data(std::initializer_list dataUsed) template TopTaskRef& TopTaskRef::func(FUNC_T funcArg) { - m_rBuilder.m_rData.resize(m_rBuilder.m_taskIds.capacity()); + m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_func = wrap_args(funcArg); return *this; } TopTaskRef& TopTaskRef::func_raw(TopTaskFunc_t func) { - m_rBuilder.m_rData.resize(m_rBuilder.m_taskIds.capacity()); + m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_func = func; return *this; } diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 7e98a0c4..7934fc86 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -50,7 +50,7 @@ namespace testapp // Scene sessions -#define OSP_DATA_TESTAPP_COMMON_SCENE 9, \ +#define TESTAPP_DATA_COMMON_SCENE 9, \ idDeltaTimeIn, idActiveIds, idBasic, idDrawing, idDrawingRes, idDelEnts, idDelTotal, idDelDrawEnts, idNMesh #define OSP_TAGS_TESTAPP_COMMON_SCENE 37, \ tgCleanupEvt, tgResyncEvt, tgSyncEvt, tgSceneEvt, tgTimeEvt, \ @@ -72,21 +72,21 @@ struct TgtScene osp::TargetId resyncAll; }; -#define OSP_DATA_TESTAPP_MATERIAL 2, \ +#define TESTAPP_DATA_MATERIAL 2, \ idMatEnts, idMatDirty #define OSP_TAGS_TESTAPP_MATERIAL 4, \ tgMatDel, tgMatMod, tgMatReq, tgMatClr -#define OSP_DATA_TESTAPP_MATERIAL 2, \ +#define TESTAPP_DATA_MATERIAL 2, \ idMatEnts, idMatDirty #define OSP_TAGS_TESTAPP_MATERIAL 4, \ tgMatDel, tgMatMod, tgMatReq, tgMatClr -#define OSP_DATA_TESTAPP_PHYSICS 3, \ +#define TESTAPP_DATA_PHYSICS 3, \ idPhys, idHierBody, idPhysIn #define OSP_TAGS_TESTAPP_PHYSICS 6, \ tgPhysPrv, tgPhysDel, tgPhysMod, tgPhysReq, \ @@ -94,7 +94,7 @@ struct TgtScene -#define OSP_DATA_TESTAPP_SHAPE_SPAWN 2, \ +#define TESTAPP_DATA_SHAPE_SPAWN 2, \ idSpawner, idSpawnerEnts #define OSP_TAGS_TESTAPP_SHAPE_SPAWN 5, \ tgSpawnMod, tgSpawnReq, tgSpawnClr, \ @@ -102,7 +102,7 @@ struct TgtScene -#define OSP_DATA_TESTAPP_PREFABS 1, \ +#define TESTAPP_DATA_PREFABS 1, \ idPrefabInit #define OSP_TAGS_TESTAPP_PREFABS 7, \ tgPrefabMod, tgPrefabReq, tgPrefabClr, \ @@ -111,7 +111,7 @@ struct TgtScene -#define OSP_DATA_TESTAPP_BOUNDS 2, \ +#define TESTAPP_DATA_BOUNDS 2, \ idBounds, idOutOfBounds #define OSP_TAGS_TESTAPP_BOUNDS 5, \ tgBoundsSetDel, tgBoundsSetMod, tgBoundsSetReq, \ @@ -119,7 +119,7 @@ struct TgtScene -#define OSP_DATA_TESTAPP_PARTS 6, \ +#define TESTAPP_DATA_PARTS 6, \ idScnParts, idPartInit, idUpdMach, idMachEvtTags, idMachUpdEnqueue, idtgNodeUpdEvt #define OSP_TAGS_TESTAPP_PARTS 17, \ tgPartMod, tgPartReq, tgPartClr, \ @@ -132,7 +132,7 @@ struct TgtScene -#define OSP_DATA_TESTAPP_VEHICLE_SPAWN 1, \ +#define TESTAPP_DATA_VEHICLE_SPAWN 1, \ idVehicleSpawn #define OSP_TAGS_TESTAPP_VEHICLE_SPAWN 11, \ tgVsBasicInMod, tgVsBasicInReq, tgVsBasicInClr, \ @@ -143,7 +143,7 @@ struct TgtScene -#define OSP_DATA_TESTAPP_VEHICLE_SPAWN_VB 1, \ +#define TESTAPP_DATA_VEHICLE_SPAWN_VB 1, \ idVehicleSpawnVB #define OSP_TAGS_TESTAPP_VEHICLE_SPAWN_VB 10, \ tgVbSpBasicInMod, tgVbSpBasicInReq, \ @@ -153,12 +153,12 @@ struct TgtScene tgVbNodeMod, tgVbNodeReq -#define OSP_DATA_TESTAPP_TEST_VEHICLES 1, \ +#define TESTAPP_DATA_TEST_VEHICLES 1, \ idTVPartVehicle -#define OSP_DATA_TESTAPP_SIGNALS_FLOAT 2, \ +#define TESTAPP_DATA_SIGNALS_FLOAT 2, \ idSigValFloat, idSigUpdFloat #define OSP_TAGS_TESTAPP_SIGNALS_FLOAT 5, \ tgSigFloatLinkMod, tgSigFloatLinkReq, \ @@ -166,7 +166,7 @@ struct TgtScene -#define OSP_DATA_TESTAPP_MACH_ROCKET 1, \ +#define TESTAPP_DATA_MACH_ROCKET 1, \ idDummy #define OSP_TAGS_TESTAPP_MACH_ROCKET 1, \ tgMhRocketEvt @@ -174,18 +174,18 @@ struct TgtScene #define OSP_TAGS_TESTAPP_MACH_RCSDRIVER 1, \ tgMhRcsDriverEvt -#define OSP_DATA_TESTAPP_NEWTON 1, \ +#define TESTAPP_DATA_NEWTON 1, \ idNwt #define OSP_TAGS_TESTAPP_NEWTON 5, \ tgNwtBodyPrv, tgNwtBodyDel, tgNwtBodyMod, tgNwtBodyReq, tgNwtBodyClr -#define OSP_DATA_TESTAPP_NEWTON_FORCES 1, \ +#define TESTAPP_DATA_NEWTON_FORCES 1, \ idNwtFactors -#define OSP_DATA_TESTAPP_NEWTON_ACCEL 1, \ +#define TESTAPP_DATA_NEWTON_ACCEL 1, \ idAcceleration @@ -196,7 +196,7 @@ struct TgtScene -#define OSP_DATA_TESTAPP_ROCKETS_NWT 1, \ +#define TESTAPP_DATA_ROCKETS_NWT 1, \ idRocketsNwt @@ -204,44 +204,45 @@ struct TgtScene // Universe sessions -#define OSP_DATA_TESTAPP_UNI_CORE 2, \ +#define TESTAPP_DATA_UNI_CORE 2, \ idUniverse, tgUniDeltaTimeIn #define OSP_TAGS_TESTAPP_UNI_CORE 4, \ tgUniUpdEvt, tgUniTimeEvt, \ tgUniTransferMod, tgUniTransferReq -#define OSP_DATA_TESTAPP_UNI_SCENEFRAME 1, \ +#define TESTAPP_DATA_UNI_SCENEFRAME 1, \ idScnFrame #define OSP_TAGS_TESTAPP_UNI_SCENEFRAME 2, \ tgScnFramePosMod, tgScnFramePosReq -#define OSP_DATA_TESTAPP_UNI_PLANETS 2, \ +#define TESTAPP_DATA_UNI_PLANETS 2, \ idPlanetMainSpace, idSatSurfaceSpaces //----------------------------------------------------------------------------- // Renderer sessions, tend to exist only when the window is open -#define OSP_DATA_TESTAPP_APP 1, \ +#define TESTAPP_DATA_WINDOW_APP 1, \ idUserInput -#define OSP_TAGS_TESTAPP_APP 2, \ - tgRenderEvt, tgInputEvt -struct TgtApplication +struct TgtWindowApp { osp::TargetId input; osp::TargetId render; }; -#define OSP_DATA_TESTAPP_APP_MAGNUM 3, \ - idUserInput, idActiveApp, idRenderGl -#define OSP_TAGS_TESTAPP_APP_MAGNUM 4, \ - tgRenderEvt, tgInputEvt, tgGlUse, tgCleanupMagnumEvt +#define TESTAPP_DATA_MAGNUM 2, \ + idActiveApp, idRenderGl + +struct TgtMagnum +{ + osp::TargetId cleanup; +}; -#define OSP_DATA_TESTAPP_COMMON_RENDERER 3, \ +#define TESTAPP_DATA_COMMON_RENDERER 3, \ idScnRender, idGroupFwd, idCamera #define OSP_TAGS_TESTAPP_COMMON_RENDERER 24, \ tgDrawGlDel, tgDrawGlMod, tgDrawGlReq, \ @@ -257,34 +258,34 @@ struct TgtApplication -#define OSP_DATA_TESTAPP_CAMERA_CTRL 1, \ +#define TESTAPP_DATA_CAMERA_CTRL 1, \ idCamCtrl #define OSP_TAGS_TESTAPP_CAMERA_CTRL 2, \ tgCamCtrlMod, tgCamCtrlReq -#define OSP_DATA_TESTAPP_SHADER_VISUALIZER 1, \ +#define TESTAPP_DATA_SHADER_VISUALIZER 1, \ idDrawShVisual -#define OSP_DATA_TESTAPP_SHADER_PHONG 1, \ +#define TESTAPP_DATA_SHADER_PHONG 1, \ idDrawShPhong -#define OSP_DATA_TESTAPP_SHADER_FLAT 1, \ +#define TESTAPP_DATA_SHADER_FLAT 1, \ idDrawShFlat -#define OSP_DATA_TESTAPP_INDICATOR 1, \ +#define TESTAPP_DATA_INDICATOR 1, \ idIndicator -#define OSP_DATA_TESTAPP_VEHICLE_CONTROL 1, \ +#define TESTAPP_DATA_VEHICLE_CONTROL 1, \ idVhControls #define OSP_TAGS_TESTAPP_VEHICLE_CONTROL 2, \ tgSelUsrCtrlMod, tgSelUsrCtrlReq diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index ca91e935..0bffef0e 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -43,6 +43,8 @@ #include #include +#include + #include #include @@ -54,37 +56,34 @@ using namespace osp; namespace testapp { -static void setup_magnum_draw(MainView mainView, Session const& application, Session const& scene, Session const& scnRender, std::vector run = {}) +static void setup_magnum_draw(Session const& windowApp, Session const& magnum, Session const& scene, Session const& scnRender, std::vector run = {}) { - OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_DATA(application, TESTAPP_APP_MAGNUM); - - Tasks &rTasks = mainView.m_rTasks; - TopTaskDataVec_t &rTaskData = mainView.m_rTaskData; - ExecContext &rExec = mainView.m_rExec; - ArrayView topData = mainView.m_topData; +#if 0 + OSP_DECLARE_GET_DATA_IDS(scnRender, TESTAPP_DATA_COMMON_RENDERER); + OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); + OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); - auto &rActiveApp = top_get(topData, idActiveApp); - auto &rCamera = top_get(topData, idCamera); + auto &rActiveApp = top_get(main.m_topData, idActiveApp); + auto &rCamera = top_get(main.m_topData, idCamera); rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); - auto tgScn = scene .get_targets(); - auto tgApp = application .get_targets(); + auto tgScn = scene .get_targets(); + auto tgApp = windowApp .get_targets(); // Run Resync tasks to mark all used gpu resources as dirty - top_enqueue_quick(rTasks, rExec, {tgScn.resyncAll}); - top_run_blocking(rTasks, rTaskData, topData, rExec); + top_enqueue_quick(main.m_rTasks, main.m_rGraph, main.m_rExec, {tgScn.resyncAll}); + top_run_blocking(main.m_rTasks, main.m_rGraph, main.m_rTaskData, main.m_topData, main.m_rExec); run.insert(run.end(), {tgScn.sync, tgScn.time, tgApp.input, tgApp.render}); // run gets copied but who cares lol - rActiveApp.set_on_draw( [&rTasks, &rExec, &rTaskData, topData, runTagsVec = std::move(run)] + rActiveApp.set_on_draw( [main, runTagsVec = std::move(run)] (ActiveApplication& rApp, float delta) { // Magnum Application's main loop is here - top_enqueue_quick(rTasks, rExec, runTagsVec); - top_run_blocking(rTasks, rTaskData, topData, rExec); + top_enqueue_quick(main.m_rTasks, main.m_rGraph, main.m_rExec, runTagsVec); + top_run_blocking(main.m_rTasks, main.m_rGraph, main.m_rTaskData, main.m_topData, main.m_rExec); // Enqueued tasks that don't run indicate a deadlock // if ( ! std::all_of(std::begin(rExec.m_taskQueuedCounts), @@ -96,37 +95,40 @@ static void setup_magnum_draw(MainView mainView, Session const& application, Ses // std::abort(); // } }); +#endif } static ScenarioMap_t make_scenarios() { ScenarioMap_t scenarioMap; - auto const add_scenario = [&scenarioMap] (std::string_view name, std::string_view desc, SceneSetup_t run) + auto const add_scenario = [&scenarioMap] (std::string_view name, std::string_view desc, SceneSetupFunc_t run) { scenarioMap.emplace(name, ScenarioOption{desc, run}); }; add_scenario("enginetest", "Basic game engine and drawing scenario (without using TopTasks)", - [] (MainView mainView, Sessions_t& sceneOut) -> RendererSetup_t + [] (TestApp& rTestApp) -> RendererSetupFunc_t { - sceneOut.resize(1); - TopDataId const idSceneData = sceneOut.front().acquire_data<1>(mainView.m_topData).front(); - auto &rResources = top_get(mainView.m_topData, mainView.m_idResources); + SessionGroup &rOut = rTestApp.m_scene; + rOut.m_sessions.resize(1); + TopDataId const idSceneData = rOut.m_sessions[0].acquire_data<1>(rTestApp.m_topData)[0]; + auto &rResources = osp::top_get(rTestApp.m_topData, rTestApp.m_idResources); // enginetest::setup_scene returns an entt::any containing one big // struct containing all the scene data. - top_assign(mainView.m_topData, idSceneData, enginetest::setup_scene(rResources, mainView.m_defaultPkg)); + top_assign(rTestApp.m_topData, idSceneData, enginetest::setup_scene(rResources, rTestApp.m_defaultPkg)); - return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, [[maybe_unused]] Sessions_t& rendererOut) + return [] (TestApp& rTestApp) { - TopDataId const idSceneData = scene.front().m_data.front(); - auto& rScene = top_get(mainView.m_topData, idSceneData); - - OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); - auto &rActiveApp = top_get< ActiveApplication > (mainView.m_topData, idActiveApp); - auto &rRenderGl = top_get< active::RenderGL > (mainView.m_topData, idRenderGl); - auto &rUserInput = top_get< input::UserInputHandler >(mainView.m_topData, idUserInput); + TopDataId const idSceneData = rTestApp.m_scene.m_sessions[0].m_data[0]; + auto &rScene = osp::top_get(rTestApp.m_topData, idSceneData); + + OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); + OSP_DECLARE_GET_DATA_IDS(rTestApp.m_windowApp, TESTAPP_DATA_WINDOW_APP); + auto &rActiveApp = top_get< ActiveApplication > (rTestApp.m_topData, idActiveApp); + auto &rRenderGl = top_get< active::RenderGL > (rTestApp.m_topData, idRenderGl); + auto &rUserInput = top_get< input::UserInputHandler >(rTestApp.m_topData, idUserInput); // Renderer state is stored as lambda capture rActiveApp.set_on_draw(enginetest::generate_draw_func(rScene, rActiveApp, rRenderGl, rUserInput)); diff --git a/src/test_application/activescenes/scenarios.h b/src/test_application/activescenes/scenarios.h index da4282d6..8d1cfc83 100644 --- a/src/test_application/activescenes/scenarios.h +++ b/src/test_application/activescenes/scenarios.h @@ -24,43 +24,21 @@ */ #pragma once -#include +#include "../testapp.h" -#include - -#include +// IWYU pragma: begin_exports #include -#include -#include -#include +// IWYU pragma: end_exports -#include -#include #include -namespace osp::active { struct RenderGL; } -namespace osp::input { class UserInputHandler; } - namespace testapp { -struct MainView -{ - osp::ArrayView m_topData; - osp::Tasks & m_rTasks; - osp::ExecutionContext & m_rExec; - osp::TopTaskDataVec_t & m_rTaskData; - osp::TopDataId m_idResources; - osp::PkgId m_defaultPkg; -}; - -using RendererSetup_t = void(*)(MainView, osp::Session const&, osp::Sessions_t const&, osp::Sessions_t&); -using SceneSetup_t = RendererSetup_t(*)(MainView, osp::Sessions_t&); - struct ScenarioOption { std::string_view m_desc; - SceneSetup_t m_setup; + SceneSetupFunc_t m_setup; }; using ScenarioMap_t = std::unordered_map; diff --git a/src/test_application/activescenes/scenarios_enginetest.cpp b/src/test_application/activescenes/scenarios_enginetest.cpp index 23e1e82e..9d8f7584 100644 --- a/src/test_application/activescenes/scenarios_enginetest.cpp +++ b/src/test_application/activescenes/scenarios_enginetest.cpp @@ -142,6 +142,8 @@ entt::any setup_scene(osp::Resources& rResources, osp::PkgId const pkg) // Add cube mesh to cube + rScene.m_drawing.m_needDrawTf.set(std::size_t(cubeEnt)); + rScene.m_drawing.m_activeToDraw[cubeEnt] = cubeDraw; rScene.m_drawing.m_mesh[cubeDraw] = rScene.m_drawing.m_meshRefCounts.ref_add(meshCube); rScene.m_drawing.m_meshDirty.push_back(cubeDraw); diff --git a/src/test_application/activescenes/scenarios_enginetest.h b/src/test_application/activescenes/scenarios_enginetest.h index cd6d8378..f1d18d13 100644 --- a/src/test_application/activescenes/scenarios_enginetest.h +++ b/src/test_application/activescenes/scenarios_enginetest.h @@ -26,6 +26,8 @@ #include "../ActiveApplication.h" +#include + namespace testapp::enginetest { diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 8d679877..5878f0e5 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -62,39 +62,56 @@ using Magnum::GL::Mesh; namespace testapp::scenes { -#if 0 +Session setup_window_app( + TopTaskBuilder& rBuilder, + ArrayView const topData) +{ + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_WINDOW_APP); + out.create_targets(rBuilder); -Session setup_magnum_application( + auto &rUserInput = osp::top_emplace(topData, idUserInput, 12); + config_controls(rUserInput); + + return out; +} + +Session setup_magnum( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& windowApp, TopDataId const idResources, ActiveApplication::Arguments args) { - Session magnum; - OSP_SESSION_ACQUIRE_DATA(magnum, topData, TESTAPP_APP_MAGNUM); - OSP_SESSION_ACQUIRE_TAGS(magnum, rTags, TESTAPP_APP_MAGNUM); + OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); + auto& rUserInput = top_get(topData, idUserInput); - // Order-dependent; ActiveApplication construction starts OpenGL context - auto &rUserInput = osp::top_emplace(topData, idUserInput, 12); - osp::top_emplace (topData, idActiveApp, args, rUserInput); - auto &rRenderGl = osp::top_emplace (topData, idRenderGl); + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_MAGNUM); + auto const tgMgn = out.create_targets(rBuilder); + out.m_cleanupTgt = tgMgn.cleanup; + + // Order-dependent; ActiveApplication construction starts OpenGL context, needed by RenderGL + /* unused */ top_emplace(topData, idActiveApp, args, rUserInput); + auto &rRenderGl = top_emplace (topData, idRenderGl); - config_controls(rUserInput); SysRenderGL::setup_context(rRenderGl); - magnum.task() = rBuilder.task().assign({tgCleanupMagnumEvt, tgGlUse}).data( - "Clean up Magnum renderer", - TopDataIds_t{ idResources, idRenderGl}, - wrap_args([] (Resources& rResources, RenderGL& rRenderGl) noexcept + rBuilder.task() + .push_to(out.m_tasks) + .depends_on({tgMgn.cleanup}) + .name("Clean up Magnum renderer") + .data({ idResources, idRenderGl}) + .func([] (Resources& rResources, RenderGL& rRenderGl) noexcept { SysRenderGL::clear_resource_owners(rRenderGl, rResources); rRenderGl = {}; // Needs the OpenGL thread for destruction - })); - magnum.m_tgCleanupEvt = tgCleanupMagnumEvt; + }); - return magnum; + return out; } +#if 0 Session setup_scene_renderer( TopTaskBuilder& rBuilder, diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index ecec7833..38ff46d3 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -32,11 +32,15 @@ namespace testapp::scenes { +osp::Session setup_window_app( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData); -osp::Session setup_magnum_application( +osp::Session setup_magnum( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::TopDataId const idResources, + osp::Session const& windowApp, + osp::TopDataId idResources, ActiveApplication::Arguments args); /** diff --git a/src/test_application/activescenes/testapp.cpp b/src/test_application/activescenes/testapp.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/test_application/activescenes/testapp.h b/src/test_application/activescenes/testapp.h new file mode 100644 index 00000000..e69de29b diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index d2adcc7b..d7c32b03 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -22,8 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "testapp.h" -#include "osp/tasks/top_utils.h" #include #include "ActiveApplication.h" @@ -35,11 +35,6 @@ #include #include -#include -#include -#include -#include - #include #include @@ -72,10 +67,8 @@ using namespace testapp; * @brief Starts a spaghetti REPL line interface that gets inputs from stdin * * This interface can be used to run commands and load scenes - * - * @return An error code maybe */ -int debug_cli_loop(); +void debug_cli_loop(); /** * @brief Starts Magnum application (ActiveApplication) thread g_magnumThread @@ -90,55 +83,17 @@ void start_magnum_async(); * This should only be called once for the entire lifetime * of the program. * - * prefer not to use names like this anywhere else but main.cpp + * prefer not to use names like this outside of testapp */ void load_a_bunch_of_stuff(); -/** - * @brief Deal with resource reference counts for a clean termination - */ -void clear_resource_owners(); - // called only from commands to display information void debug_print_help(); void debug_print_resources(); -// TopData stores most application state, addressed using a TopDataId -std::vector g_appTopData; - -// TopTasks and are organized with Tags to form task graphs and events. -// Each TopTask is given a vector of TopDataIds its allowed to access -osp::Tasks g_tasks; -osp::TopTaskDataVec_t g_taskData; - -// Current execution state of TopTasks -// g_tasks, g_taskData, and g_tags stay constant during execution -osp::ExecContext g_exec; - -// Sessions bundle together and own TopDataIds, TopTaskIds, and TagsIds -// Sessions intend to add support for something to exist in the world -// eg, Adding support for physics or supporting a certain shader +TestApp g_testApp; -// Application Session lasts the lifetime of the program. -// It stores the Resources class, addressable by a TopDataId -osp::Session g_application; -osp::TopDataId g_idResources{lgrn::id_null()}; - -// Default Package in Resources -osp::PkgId g_defaultPkg; - -// Sessions that make up the current scene -osp::Sessions_t g_sceneSessions; - -// Sessions for rendering. These only exist when the Magnum Application is open -osp::Session g_magnum; -osp::Sessions_t g_renderSessions; - -// Magnum Application deals with window and OpenGL things -std::thread g_magnumThread; - -// Called when openning a Magnum Application -RendererSetup_t g_rendererSetup; +std::thread g_magnumThread; // Loggers std::shared_ptr g_logTestApp; @@ -148,29 +103,6 @@ std::shared_ptr g_logMagnumApp; int g_argc; char** g_argv; -static void close_sessions(osp::Sessions_t &rSessions) -{ - osp::top_close_session(g_tasks, g_taskData, g_appTopData, g_exec, rSessions); - rSessions.clear(); -} - -static void close_session(osp::Session &rSession) -{ - osp::top_close_session(g_tasks, g_taskData, g_appTopData, g_exec, osp::ArrayView(&rSession, 1)); -} - -static MainView get_main_view() -{ - return { - .m_topData = g_appTopData, - .m_rTasks = g_tasks, - .m_rExec = g_exec, - .m_rTaskData = g_taskData, - .m_idResources = g_idResources, - .m_defaultPkg = g_defaultPkg - }; -} - int main(int argc, char** argv) { @@ -197,7 +129,7 @@ int main(int argc, char** argv) osp::set_thread_logger(g_logTestApp); // Set logger for this thread - g_appTopData.resize(64); + g_testApp.m_topData.resize(64); load_a_bunch_of_stuff(); if(args.value("scene") != "none") @@ -206,11 +138,11 @@ int main(int argc, char** argv) if(it == std::end(scenarios())) { std::cerr << "unknown scene" << std::endl; - clear_resource_owners(); + clear_resource_owners(g_testApp); exit(-1); } - g_rendererSetup = it->second.m_setup(get_main_view(), g_sceneSessions); + g_testApp.m_rendererSetup = it->second.m_setup(g_testApp); start_magnum_async(); } @@ -232,7 +164,7 @@ int main(int argc, char** argv) return 0; } -int debug_cli_loop() +void debug_cli_loop() { debug_print_help(); @@ -243,7 +175,7 @@ int debug_cli_loop() std::cout << "> "; std::cin >> command; - bool magnumOpen = ! g_magnum.m_data.empty(); + bool magnumOpen = ! g_testApp.m_renderer.m_sessions.empty(); if (auto const it = scenarios().find(command); it != std::end(scenarios())) { @@ -259,9 +191,9 @@ int debug_cli_loop() { std::cout << "Loading scene: " << it->first << "\n"; - close_sessions(g_sceneSessions); // Close existing scene + close_sessions(g_testApp, g_testApp.m_scene); // Close existing scene - g_rendererSetup = it->second.m_setup(get_main_view(), g_sceneSessions); + g_testApp.m_rendererSetup = it->second.m_setup(g_testApp); start_magnum_async(); } } @@ -275,7 +207,7 @@ int debug_cli_loop() { std::cout << "Application is already open\n"; } - else if ( ! bool(g_rendererSetup) ) + else if ( g_testApp.m_rendererSetup == nullptr ) { std::cout << "No existing scene loaded\n"; } @@ -294,8 +226,8 @@ int debug_cli_loop() if (magnumOpen) { // Request exit if application exists - OSP_SESSION_UNPACK_DATA(g_magnum, TESTAPP_APP_MAGNUM); // declares idActiveApp - osp::top_get(g_appTopData, idActiveApp).exit(); + OSP_DECLARE_GET_DATA_IDS(g_testApp.m_renderer.m_sessions[1], TESTAPP_DATA_MAGNUM); // declares idActiveApp + osp::top_get(g_testApp.m_topData, idActiveApp).exit(); } break; @@ -306,9 +238,7 @@ int debug_cli_loop() } } - clear_resource_owners(); - - return 0; + clear_resource_owners(g_testApp); } void start_magnum_async() @@ -322,13 +252,21 @@ void start_magnum_async() osp::set_thread_logger(g_logMagnumApp); // Start Magnum application session - osp::TopTaskBuilder builder{g_taskData}; - g_magnum = scenes::setup_magnum_application(builder, g_appTopData, g_idResources, {g_argc, g_argv}); - OSP_SESSION_UNPACK_DATA(g_magnum, TESTAPP_APP_MAGNUM); // declares idActiveApp - auto &rActiveApp = osp::top_get(g_appTopData, idActiveApp); + osp::TopTaskBuilder builder{g_testApp.m_tasks, g_testApp.m_renderer.m_edges, g_testApp.m_taskData}; + + g_testApp.m_windowApp = scenes::setup_window_app (builder, g_testApp.m_topData); + g_testApp.m_magnum = scenes::setup_magnum (builder, g_testApp.m_topData, g_testApp.m_windowApp, g_testApp.m_idResources, {g_argc, g_argv}); + + OSP_DECLARE_GET_DATA_IDS(g_testApp.m_magnum, TESTAPP_DATA_MAGNUM); // declares idActiveApp + auto &rActiveApp = osp::top_get(g_testApp.m_topData, idActiveApp); // Setup renderer sessions - g_rendererSetup(get_main_view(), g_magnum, g_sceneSessions, g_renderSessions); + + g_testApp.m_exec.resize(g_testApp.m_tasks); + g_testApp.m_graph.reset(); + g_testApp.m_graph.emplace(osp::make_exec_graph(g_testApp.m_tasks, {&g_testApp.m_renderer.m_edges, &g_testApp.m_scene.m_edges})); + + g_testApp.m_rendererSetup(g_testApp); // Starts the main loop. This function is blocking, and will only return // once the window is closed. See ActiveApplication::drawEvent @@ -339,8 +277,9 @@ void start_magnum_async() rActiveApp.set_on_draw({}); // Closing sessions will delete their associated TopData and Tags - close_sessions(g_renderSessions); - close_session(g_magnum); + close_sessions(g_testApp, g_testApp.m_renderer); + close_session(g_testApp, g_testApp.m_magnum); + close_session(g_testApp, g_testApp.m_windowApp); OSP_LOG_INFO("Closed Magnum Application"); }); @@ -359,17 +298,9 @@ void load_a_bunch_of_stuff() std::size_t const maxTags = 256; // aka: just two 64-bit integers std::size_t const maxTagsInts = maxTags / 64; - g_tags.m_tags.reserve(maxTags); - g_tags.m_tagDepends.resize(maxTags * g_tags.m_tagDependsPerTag, - lgrn::id_null()); - g_tags.m_tagLimits.resize(maxTagsInts); - g_tags.m_tagExtern.resize(maxTagsInts); - - - auto const [idResources] = g_application.acquire_data<1>(g_appTopData); - g_idResources = idResources; + g_testApp.m_idResources = osp::top_reserve(g_testApp.m_topData); - auto &rResources = osp::top_emplace(g_appTopData, idResources); + auto &rResources = osp::top_emplace(g_testApp.m_topData, g_testApp.m_idResources); rResources.resize_types(osp::ResTypeIdReg_t::size()); @@ -380,7 +311,7 @@ void load_a_bunch_of_stuff() rResources.data_register(gc_importer); rResources.data_register(gc_importer); osp::register_tinygltf_resources(rResources); - g_defaultPkg = rResources.pkg_create(); + g_testApp.m_defaultPkg = rResources.pkg_create(); // Load sturdy glTF files const std::string_view datapath = {"OSPData/adera/"}; @@ -400,14 +331,14 @@ void load_a_bunch_of_stuff() // images, textures, and other relevant data into osp::Resources for (auto const& meshName : meshes) { - osp::ResId res = osp::load_tinygltf_file(osp::string_concat(datapath, meshName), rResources, g_defaultPkg); + osp::ResId res = osp::load_tinygltf_file(osp::string_concat(datapath, meshName), rResources, g_testApp.m_defaultPkg); osp::assigns_prefabs_tinygltf(rResources, res); } // Add a default primitives auto const add_mesh_quick = [&rResources = rResources] (std::string_view const name, Trade::MeshData&& data) { - osp::ResId const meshId = rResources.create(gc_mesh, g_defaultPkg, osp::SharedString::create(name)); + osp::ResId const meshId = rResources.create(gc_mesh, g_testApp.m_defaultPkg, osp::SharedString::create(name)); rResources.data_add(gc_mesh, meshId, std::move(data)); }; @@ -424,65 +355,6 @@ void load_a_bunch_of_stuff() OSP_LOG_INFO("Resource loading complete"); } -using each_res_id_t = void(*)(osp::ResId); - -static void resource_for_each_type(osp::ResTypeId const type, each_res_id_t const do_thing) -{ - auto &rResources = osp::top_get(g_appTopData, g_idResources); - lgrn::IdRegistry const &rReg = rResources.ids(type); - for (std::size_t i = 0; i < rReg.capacity(); ++i) - { - if (rReg.exists(osp::ResId(i))) - { - do_thing(osp::ResId(i)); - } - } -} - -void clear_resource_owners() -{ - using namespace osp::restypes; - - // Texture resources contain osp::TextureImgSource, which refererence counts - // their associated image data - resource_for_each_type(gc_texture, [] (osp::ResId const id) - { - auto &rResources = osp::top_get(g_appTopData, g_idResources); - - auto * const pData = rResources - .data_try_get(gc_texture, id); - if (pData != nullptr) - { - rResources.owner_destroy(gc_image, std::move(*pData)); - } - }); - - // Importer data own a lot of other resources - resource_for_each_type(gc_importer, [] (osp::ResId const id) - { - auto &rResources = osp::top_get(g_appTopData, g_idResources); - - auto * const pData = rResources - .data_try_get(gc_importer, id); - if (pData != nullptr) - { - for (osp::ResIdOwner_t &rOwner : std::move(pData->m_images)) - { - rResources.owner_destroy(gc_image, std::move(rOwner)); - } - - for (osp::ResIdOwner_t &rOwner : std::move(pData->m_textures)) - { - rResources.owner_destroy(gc_texture, std::move(rOwner)); - } - - for (osp::ResIdOwner_t &rOwner : std::move(pData->m_meshes)) - { - rResources.owner_destroy(gc_mesh, std::move(rOwner)); - } - } - }); -} //----------------------------------------------------------------------------- diff --git a/src/test_application/testapp.cpp b/src/test_application/testapp.cpp new file mode 100644 index 00000000..7e3045af --- /dev/null +++ b/src/test_application/testapp.cpp @@ -0,0 +1,115 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * 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 "testapp.h" + +#include + +#include +#include + +namespace testapp +{ + +void close_sessions(TestAppTasks &rTestApp, osp::SessionGroup &rSessions) +{ + rSessions.m_edges.m_semaphoreEdges .clear(); + rSessions.m_edges.m_targetDependEdges .clear(); + rSessions.m_edges.m_targetFulfillEdges .clear(); + + if ( rSessions.m_sessions.empty() || ! rTestApp.m_graph.has_value() ) + { + return; + } + + TestApp testapp; + testapp.m_topData.clear(); + + osp::top_close_session(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec, rSessions.m_sessions); + + rSessions.m_sessions.clear(); +} + + +void close_session(TestAppTasks &rTestApp, osp::Session &rSession) +{ + osp::top_close_session(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec, osp::ArrayView(&rSession, 1)); +} + + +template +static void resource_for_each_type(osp::ResTypeId const type, osp::Resources& rResources, FUNC_T&& do_thing) +{ + lgrn::IdRegistry const &rReg = rResources.ids(type); + for (std::size_t i = 0; i < rReg.capacity(); ++i) + { + if (rReg.exists(osp::ResId(i))) + { + do_thing(osp::ResId(i)); + } + } +} + +void clear_resource_owners(TestApp& rTestApp) +{ + using namespace osp::restypes; + + auto &rResources = osp::top_get(rTestApp.m_topData, rTestApp.m_idResources); + + // Texture resources contain osp::TextureImgSource, which refererence counts + // their associated image data + resource_for_each_type(gc_texture, rResources, [&rResources] (osp::ResId const id) + { + auto * const pData = rResources.data_try_get(gc_texture, id); + if (pData != nullptr) + { + rResources.owner_destroy(gc_image, std::move(*pData)); + } + }); + + // Importer data own a lot of other resources + resource_for_each_type(gc_importer, rResources, [&rResources] (osp::ResId const id) + { + auto * const pData = rResources.data_try_get(gc_importer, id); + if (pData != nullptr) + { + for (osp::ResIdOwner_t &rOwner : std::move(pData->m_images)) + { + rResources.owner_destroy(gc_image, std::move(rOwner)); + } + + for (osp::ResIdOwner_t &rOwner : std::move(pData->m_textures)) + { + rResources.owner_destroy(gc_texture, std::move(rOwner)); + } + + for (osp::ResIdOwner_t &rOwner : std::move(pData->m_meshes)) + { + rResources.owner_destroy(gc_mesh, std::move(rOwner)); + } + } + }); +} + +} // namespace testapp diff --git a/src/test_application/testapp.h b/src/test_application/testapp.h new file mode 100644 index 00000000..dfcdc926 --- /dev/null +++ b/src/test_application/testapp.h @@ -0,0 +1,93 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * 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 + +#include + +#include + +#include +#include +#include + +#include + +namespace testapp +{ + +struct TestApp; + +using RendererSetupFunc_t = void(*)(TestApp&); + +using SceneSetupFunc_t = RendererSetupFunc_t(*)(TestApp&); + +// TopData stores most application state, addressed using a TopDataId + +// Sessions bundle together and own TopDataIds, TopTaskIds, and TagsIds +// Sessions intend to add support for something to exist in the world +// eg, Adding support for physics or supporting a certain shader + +// Current execution state of TopTasks + +// TopTasks and are organized with Tags to form task graphs and events. +// Each TopTask is given a vector of TopDataIds its allowed to access +// Called when openning a Magnum Application + + +struct TestAppTasks +{ + std::vector m_topData; + osp::Tasks m_tasks; + osp::TopTaskDataVec_t m_taskData; + osp::ExecContext m_exec; + std::optional m_graph; +}; + +struct TestApp : TestAppTasks +{ + osp::SessionGroup m_scene; + + osp::Session m_windowApp; + osp::Session m_magnum; + osp::SessionGroup m_renderer; + + RendererSetupFunc_t m_rendererSetup { nullptr }; + + osp::TopDataId m_idResources { lgrn::id_null() }; + osp::PkgId m_defaultPkg { lgrn::id_null() }; +}; + +void close_sessions(TestAppTasks &rTestApp, osp::SessionGroup &rSessions); + +void close_session(TestAppTasks &rTestApp, osp::Session &rSession); + +/** + * @brief Deal with resource reference counts for a clean termination + */ +void clear_resource_owners(TestApp& rTestApp); + +} // namespace testapp diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index f7af7e9b..9b36b80e 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -187,6 +187,7 @@ TEST(Tasks, BasicSingleThreaded) TaskEdges edges; TaskFuncVec_t functions; Builder_t builder{tasks, edges, functions}; + auto const tgt = builder.create_targets(); // Start adding tasks. The order these are added does not matter. @@ -263,7 +264,7 @@ TEST(Tasks, BasicSingleThreaded) // Execute - TestWorld world; + TestWorld world; ExecContext exec; exec.resize(tasks); From 76e754ed9a670646f71bca582bad50fd2e86a886 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Thu, 27 Apr 2023 19:03:44 -0700 Subject: [PATCH 14/35] Use entt::sparse_set for queued tasks --- src/osp/tasks/execute.cpp | 11 +++++------ src/osp/tasks/execute.h | 8 ++++++-- src/osp/tasks/top_execute.cpp | 8 ++------ src/test_application/activescenes/testapp.cpp | 0 src/test_application/activescenes/testapp.h | 0 test/tasks/main.cpp | 12 +++++------- 6 files changed, 18 insertions(+), 21 deletions(-) delete mode 100644 src/test_application/activescenes/testapp.cpp delete mode 100644 src/test_application/activescenes/testapp.h diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 608466c5..552605fd 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -29,9 +29,9 @@ namespace osp { -bool try_enqueue_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task) noexcept +static bool try_enqueue_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task) noexcept { - if (rExec.m_tasksQueued.test(std::size_t(task))) + if (rExec.m_tasksQueued.contains(task)) { return false; // task already queued } @@ -55,7 +55,7 @@ bool try_enqueue_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &r ++ rExec.m_targetPendingCount[fulfill]; } - rExec.m_tasksQueued.set(std::size_t(task)); + rExec.m_tasksQueued.emplace(task); return true; } @@ -91,7 +91,7 @@ int enqueue_dirty(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec void mark_completed_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept { - LGRN_ASSERTMV(rExec.m_tasksQueued.test(std::size_t(task)), + LGRN_ASSERTMV(rExec.m_tasksQueued.contains(task), "Task must be queued to have been allowed to run", std::size_t(task)); for (TargetId const dependOn : graph.m_taskDependOn[std::size_t(task)]) @@ -121,8 +121,7 @@ void mark_completed_task(Tasks const& tasks, ExecGraph const& graph, ExecContext } } - rExec.m_tasksQueued.reset(std::size_t(task)); + rExec.m_tasksQueued.erase(task); } - } // namespace osp diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 234b8006..f1911584 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -31,6 +31,8 @@ #include #include +#include + #include #include @@ -44,14 +46,16 @@ struct ExecContext std::size_t const maxTargets = tasks.m_targetIds.capacity(); std::size_t const maxTasks = tasks.m_taskIds.capacity(); - bitvector_resize(m_tasksQueued, maxTasks); + m_tasksQueued.reserve(maxTasks); + bitvector_resize(m_targetDirty, maxTargets); bitvector_resize(m_targetWillBePending, maxTargets); m_targetPendingCount.resize(maxTargets); m_targetInUseCount .resize(maxTargets); } - BitVector_t m_tasksQueued; + entt::basic_sparse_set m_tasksQueued; + BitVector_t m_targetDirty; BitVector_t m_targetWillBePending; KeyedVec m_targetPendingCount; diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index 8ef14259..bdc663ab 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -46,16 +46,12 @@ void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec // Run until there's no tasks left to run while (true) { - // Choose first available task - auto const enqueuedTasks = rExec.m_tasksQueued.ones(); - auto const taskIt = enqueuedTasks.begin(); - - if (taskIt == enqueuedTasks.end()) + if (rExec.m_tasksQueued.empty()) { break; } - auto const task = TaskId(*taskIt); + TaskId const task = rExec.m_tasksQueued.at(0); TopTask &rTopTask = rTaskData[task]; diff --git a/src/test_application/activescenes/testapp.cpp b/src/test_application/activescenes/testapp.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test_application/activescenes/testapp.h b/src/test_application/activescenes/testapp.h deleted file mode 100644 index e69de29b..00000000 diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 9b36b80e..1a4c4db1 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -50,23 +51,20 @@ bool contains(RANGE_T const& range, VALUE_T const& value) noexcept template void randomized_singlethreaded_execute(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, std::mt19937 &rRand, int maxRuns, RUN_TASK_T && runTask) { - std::size_t tasksLeft = rExec.m_tasksQueued.count(); - for (int i = 0; i < maxRuns; ++i) { + std::size_t const tasksLeft = rExec.m_tasksQueued.size(); + if (tasksLeft == 0) { break; } - // This solution of "pick random '1' bit in a bit vector" is very inefficient - auto const randomTask = TaskId(*std::next(rExec.m_tasksQueued.ones().begin(), rRand() % tasksLeft)); + TaskId const randomTask = rExec.m_tasksQueued.at(rRand() % tasksLeft); FulfillDirty_t const status = runTask(randomTask); mark_completed_task(tasks, graph, rExec, randomTask, status); - int const newTasks = enqueue_dirty(tasks, graph, rExec); - - tasksLeft = tasksLeft - 1 + newTasks; + enqueue_dirty(tasks, graph, rExec); } } From 6f32a960fb7d9d7b735bc890d8d1869189e0501e Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 8 May 2023 00:09:18 -0700 Subject: [PATCH 15/35] Fix Physics test scene for task changes --- src/newtondynamics_physics/SysNewton.cpp | 1 - src/osp/Active/SysPrefabInit.cpp | 4 +- src/osp/Active/SysRender.cpp | 11 +- src/osp/Active/SysRender.h | 7 +- src/osp/Active/activetypes.h | 31 +- src/osp/Active/basic.h | 8 +- src/osp/Active/drawing.h | 16 +- src/osp/Active/opengl/SysRenderGL.cpp | 6 +- src/osp/Active/opengl/SysRenderGL.h | 16 +- src/osp/Active/physics.h | 4 +- src/osp/Shaders/Flat.h | 2 +- src/osp/Shaders/MeshVisualizer.h | 4 +- src/osp/Shaders/Phong.h | 2 +- src/osp/tasks/builder.h | 18 +- src/osp/tasks/execute.cpp | 60 +++- src/osp/tasks/execute.h | 16 +- src/osp/tasks/tasks.cpp | 14 +- src/osp/tasks/tasks.h | 19 +- src/osp/tasks/top_execute.cpp | 66 +++- src/osp/tasks/top_execute.h | 4 +- src/osp/tasks/top_session.cpp | 2 +- src/osp/tasks/top_session.h | 11 +- src/osp/tasks/top_tasks.h | 3 +- src/osp/tasks/top_utils.h | 21 +- src/osp/tasks/top_worker.h | 17 +- .../activescenes/identifiers.h | 142 ++++---- .../activescenes/scenarios.cpp | 227 +++++++----- .../activescenes/scenarios_enginetest.cpp | 2 +- .../activescenes/scene_common.cpp | 320 +++++++---------- .../activescenes/scene_common.h | 22 +- .../activescenes/scene_misc.cpp | 179 ++++++---- .../activescenes/scene_misc.h | 24 +- .../activescenes/scene_newton.cpp | 336 ++++++++++-------- .../activescenes/scene_newton.h | 9 +- .../activescenes/scene_physics.cpp | 242 ++++++------- .../activescenes/scene_physics.h | 19 +- .../activescenes/scene_renderer.cpp | 289 ++++++++------- .../activescenes/scene_renderer.h | 12 +- .../activescenes/scene_vehicles.cpp | 38 +- .../activescenes/scene_vehicles.h | 18 +- src/test_application/main.cpp | 13 +- src/test_application/testapp.cpp | 4 +- src/test_application/testapp.h | 14 - test/tasks/main.cpp | 16 +- 44 files changed, 1211 insertions(+), 1078 deletions(-) diff --git a/src/newtondynamics_physics/SysNewton.cpp b/src/newtondynamics_physics/SysNewton.cpp index 37bef6a3..ce13288c 100644 --- a/src/newtondynamics_physics/SysNewton.cpp +++ b/src/newtondynamics_physics/SysNewton.cpp @@ -55,7 +55,6 @@ using namespace Magnum::Math::Literals; using osp::phys::EShape; -using osp::active::acomp_view_t; using osp::active::acomp_storage_t; using osp::active::ActiveEnt; diff --git a/src/osp/Active/SysPrefabInit.cpp b/src/osp/Active/SysPrefabInit.cpp index ecba202b..a3461b7f 100644 --- a/src/osp/Active/SysPrefabInit.cpp +++ b/src/osp/Active/SysPrefabInit.cpp @@ -218,8 +218,8 @@ void SysPrefabInit::init_drawing( if (material.has_value()) { - material.value().m_rEnts.set(std::size_t(drawEnt)); - material.value().m_rDirty.push_back(drawEnt); + material.value().m_ents.set(std::size_t(drawEnt)); + material.value().m_dirty.push_back(drawEnt); } } diff --git a/src/osp/Active/SysRender.cpp b/src/osp/Active/SysRender.cpp index 0085605c..3326b10d 100644 --- a/src/osp/Active/SysRender.cpp +++ b/src/osp/Active/SysRender.cpp @@ -108,6 +108,15 @@ void SysRender::set_dirty_all(ACtxDrawing &rCtxDrawing) rCtxDrawing.m_diffuseDirty.push_back(drawEnt); } } + + for (std::size_t const materialInt : rCtxDrawing.m_materialIds.bitview().zeros()) + { + Material &mat = rCtxDrawing.m_materials[MaterialId(materialInt)]; + for (std::size_t const entInt : mat.m_ents.ones()) + { + mat.m_dirty.push_back(DrawEnt(entInt)); + } + } } void SysRender::clear_dirty_all(ACtxDrawing& rCtxDrawing) @@ -122,7 +131,7 @@ void SysRender::update_draw_transforms_recurse( KeyedVec const& activeToDraw, acomp_storage_t const& rTf, DrawTransforms_t& rDrawTf, - EntSet_t const& needDrawTf, + ActiveEntSet_t const& needDrawTf, ActiveEnt ent, Matrix4 const& parentTf, bool root) diff --git a/src/osp/Active/SysRender.h b/src/osp/Active/SysRender.h index df00c47f..25602331 100644 --- a/src/osp/Active/SysRender.h +++ b/src/osp/Active/SysRender.h @@ -25,6 +25,7 @@ #pragma once #include "drawing.h" +#include "basic.h" #include @@ -178,7 +179,7 @@ class SysRender KeyedVec const& activeToDraw, acomp_storage_t const& transform, DrawTransforms_t& rDrawTf, - EntSet_t const& useDrawTf, + ActiveEntSet_t const& useDrawTf, IT_T first, ITB_T const& last); @@ -214,7 +215,7 @@ class SysRender KeyedVec const& activeToDraw, acomp_storage_t const& rTf, DrawTransforms_t& rDrawTf, - EntSet_t const& useDrawTf, + ActiveEntSet_t const& useDrawTf, ActiveEnt ent, Matrix4 const& parentTf, bool root); @@ -241,7 +242,7 @@ void SysRender::update_draw_transforms( KeyedVec const& activeToDraw, acomp_storage_t const& rTf, DrawTransforms_t& rDrawTf, - EntSet_t const& needDrawTf, + ActiveEntSet_t const& needDrawTf, IT_T first, ITB_T const& last) { diff --git a/src/osp/Active/activetypes.h b/src/osp/Active/activetypes.h index e27d649c..4765a7a3 100644 --- a/src/osp/Active/activetypes.h +++ b/src/osp/Active/activetypes.h @@ -43,35 +43,26 @@ namespace osp::active { -enum class ActiveEnt: entt::id_type {}; +enum class ActiveEnt: uint32_t { }; -} // namespace osp::active +using ActiveEntVec_t = std::vector; +using ActiveEntSet_t = BitVector_t; +using active_sparse_set_t = entt::basic_sparse_set; -// Specialize entt::storage_traits to disable signals for storage that uses -// ActiveEnt as entities -template -struct entt::storage_type -{ - using type = basic_storage; -}; +template +using acomp_storage_t = typename entt::basic_storage; +//----------------------------------------------------------------------------- -namespace osp::active -{ - -using ActiveReg_t = lgrn::IdRegistryStl; +enum class DrawEnt : uint32_t { }; -using EntVector_t = std::vector; -using EntSet_t = BitVector_t; +using DrawEntVec_t = std::vector; +using DrawEntSet_t = BitVector_t; -using active_sparse_set_t = entt::basic_sparse_set; -template -using acomp_storage_t = typename entt::storage_type::type; +enum class MaterialId : uint32_t { }; -template -using acomp_view_t = entt::basic_view, entt::exclude_t<>>; } // namespace osp::active diff --git a/src/osp/Active/basic.h b/src/osp/Active/basic.h index 66a37a2c..23a84b20 100644 --- a/src/osp/Active/basic.h +++ b/src/osp/Active/basic.h @@ -79,17 +79,15 @@ struct ACtxSceneGraph */ struct ACtxBasic { - ACtxSceneGraph m_scnGraph; + lgrn::IdRegistryStl m_activeIds; - acomp_storage_t m_transform; - acomp_storage_t m_name; + ACtxSceneGraph m_scnGraph; + acomp_storage_t m_transform; }; template void update_delete_basic(ACtxBasic &rCtxBasic, IT_T first, IT_T const& last) { - rCtxBasic.m_name .remove(first, last); - while (first != last) { ActiveEnt const ent = *first; diff --git a/src/osp/Active/drawing.h b/src/osp/Active/drawing.h index 13b9a832..1f6418ba 100644 --- a/src/osp/Active/drawing.h +++ b/src/osp/Active/drawing.h @@ -25,7 +25,6 @@ #pragma once #include "activetypes.h" -#include "basic.h" #include "../bitvector.h" #include "../id_map.h" @@ -42,10 +41,6 @@ namespace osp::active { -enum class DrawEnt : uint32_t { }; - -using DrawEntVector_t = std::vector; -using DrawEntSet_t = BitVector_t; struct BasicDrawProps { @@ -55,8 +50,8 @@ struct BasicDrawProps struct Material { - EntSet_t& m_rEnts; - std::vector& m_rDirty; + DrawEntSet_t m_ents; + DrawEntVec_t m_dirty; }; /** @@ -105,11 +100,11 @@ struct ACtxDrawing lgrn::IdRegistryStl m_drawIds; - EntSet_t m_visible; + DrawEntSet_t m_visible; KeyedVec m_drawBasic; KeyedVec m_color; - EntSet_t m_needDrawTf; + DrawEntSet_t m_needDrawTf; KeyedVec m_activeToDraw; // Scene-space Meshes @@ -126,6 +121,9 @@ struct ACtxDrawing KeyedVec m_mesh; std::vector m_meshDirty; + + lgrn::IdRegistryStl m_materialIds; + KeyedVec m_materials; }; /** diff --git a/src/osp/Active/opengl/SysRenderGL.cpp b/src/osp/Active/opengl/SysRenderGL.cpp index 17e58765..3588cc68 100644 --- a/src/osp/Active/opengl/SysRenderGL.cpp +++ b/src/osp/Active/opengl/SysRenderGL.cpp @@ -346,7 +346,7 @@ void SysRenderGL::clear_resource_owners(RenderGL& rRenderGl, Resources& rResourc void SysRenderGL::render_opaque( RenderGroup const& group, - EntSet_t const& visible, + DrawEntSet_t const& visible, ViewProjMatrix const& viewProj) { using Magnum::GL::Renderer; @@ -361,7 +361,7 @@ void SysRenderGL::render_opaque( void SysRenderGL::render_transparent( RenderGroup const& group, - EntSet_t const& visible, + DrawEntSet_t const& visible, ViewProjMatrix const& viewProj) { using Magnum::GL::Renderer; @@ -382,7 +382,7 @@ void SysRenderGL::render_transparent( void SysRenderGL::draw_group( RenderGroup const& group, - EntSet_t const& visible, + DrawEntSet_t const& visible, ViewProjMatrix const& viewProj) { for (auto const& [ent, toDraw] : group.view().each()) diff --git a/src/osp/Active/opengl/SysRenderGL.h b/src/osp/Active/opengl/SysRenderGL.h index 10f21537..c71b46b9 100644 --- a/src/osp/Active/opengl/SysRenderGL.h +++ b/src/osp/Active/opengl/SysRenderGL.h @@ -189,7 +189,7 @@ class SysRenderGL */ static void render_opaque( RenderGroup const& group, - EntSet_t const& visible, + DrawEntSet_t const& visible, ViewProjMatrix const& viewProj); /** @@ -203,26 +203,16 @@ class SysRenderGL */ static void render_transparent( RenderGroup const& group, - EntSet_t const& visible, + DrawEntSet_t const& visible, ViewProjMatrix const& viewProj); static void draw_group( RenderGroup const& group, - EntSet_t const& visible, + DrawEntSet_t const& visible, ViewProjMatrix const& viewProj); - template - static void update_delete( - ACtxSceneRenderGL &rCtxRenderGl, IT_T first, IT_T const& last); }; -template -void SysRenderGL::update_delete( - ACtxSceneRenderGL &rCtxRenderGl, IT_T first, IT_T const& last) -{ - //rCtxRenderGl.m_drawTransform .remove(first, last); -} - } diff --git a/src/osp/Active/physics.h b/src/osp/Active/physics.h index 0c96fd3c..deba606a 100644 --- a/src/osp/Active/physics.h +++ b/src/osp/Active/physics.h @@ -48,14 +48,14 @@ struct ACompMass struct ACtxPhysics { std::vector m_shape; - EntSet_t m_hasColliders; + ActiveEntSet_t m_hasColliders; acomp_storage_t m_mass; Vector3 m_originTranslate; - EntVector_t m_colliderDirty; + ActiveEntVec_t m_colliderDirty; std::vector< std::pair > m_setVelocity; diff --git a/src/osp/Shaders/Flat.h b/src/osp/Shaders/Flat.h index cecb897f..e3ab7f3f 100644 --- a/src/osp/Shaders/Flat.h +++ b/src/osp/Shaders/Flat.h @@ -86,7 +86,7 @@ template void sync_flat( ITA_T dirtyIt, ITB_T const& dirtyLast, - active::EntSet_t const& hasMaterial, + active::DrawEntSet_t const& hasMaterial, active::RenderGroup::Storage_t *const pStorageOpaque, active::RenderGroup::Storage_t *const pStorageTransparent, KeyedVec const& drawBasic, diff --git a/src/osp/Shaders/MeshVisualizer.h b/src/osp/Shaders/MeshVisualizer.h index f24247f8..e4c53605 100644 --- a/src/osp/Shaders/MeshVisualizer.h +++ b/src/osp/Shaders/MeshVisualizer.h @@ -41,6 +41,8 @@ struct ACtxDrawMeshVisualizer osp::active::MeshGlEntStorage_t *m_pMeshId{nullptr}; osp::active::MeshGlStorage_t *m_pMeshGl{nullptr}; + osp::active::MaterialId m_materialId { lgrn::id_null() }; + bool m_wireframeOnly{false}; constexpr void assign_pointers(active::ACtxSceneRenderGL& rCtxScnGl, @@ -61,7 +63,7 @@ template void sync_visualizer( ITA_T dirtyFirst, ITB_T const& dirtyLast, - active::EntSet_t const& hasMaterial, + active::DrawEntSet_t const& hasMaterial, active::RenderGroup::Storage_t& rStorage, ACtxDrawMeshVisualizer &rData) { diff --git a/src/osp/Shaders/Phong.h b/src/osp/Shaders/Phong.h index aff04173..50ea190e 100644 --- a/src/osp/Shaders/Phong.h +++ b/src/osp/Shaders/Phong.h @@ -87,7 +87,7 @@ template void sync_phong( ITA_T dirtyIt, ITB_T const& dirtyLast, - active::EntSet_t const& hasMaterial, + active::DrawEntSet_t const& hasMaterial, active::RenderGroup::Storage_t *const pStorageOpaque, active::RenderGroup::Storage_t *const pStorageTransparent, KeyedVec const& drawBasic, diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 1fbccdc4..b6a8855b 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -99,11 +99,25 @@ struct TaskRefBase constexpr Tasks & tasks() noexcept { return m_rBuilder.m_rTasks; } + TASKREF_T& trigger_on(ArrayView const targets) noexcept + { + for (TargetId const target : targets) + { + m_rBuilder.m_rEdges.m_targetDependEdges.push_back({m_taskId, target, true}); + } + return static_cast(*this); + } + + TASKREF_T& trigger_on(std::initializer_list targets) noexcept + { + return trigger_on(Corrade::Containers::arrayView(targets)); + } + TASKREF_T& depends_on(ArrayView const targets) noexcept { for (TargetId const target : targets) { - m_rBuilder.m_rEdges.m_targetDependEdges.push_back({m_taskId, target}); + m_rBuilder.m_rEdges.m_targetDependEdges.push_back({m_taskId, target, false}); } return static_cast(*this); } @@ -117,7 +131,7 @@ struct TaskRefBase { for (TargetId const target : targets) { - m_rBuilder.m_rEdges.m_targetFulfillEdges.push_back({m_taskId, target}); + m_rBuilder.m_rEdges.m_targetFulfillEdges.push_back({m_taskId, target, false}); } return static_cast(*this); } diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 552605fd..eeee509a 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -29,25 +29,54 @@ namespace osp { -static bool try_enqueue_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task) noexcept +static bool try_enqueue_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task, TargetId const calledFrom) noexcept { if (rExec.m_tasksQueued.contains(task)) { return false; // task already queued } - for (TargetId const dependOn : graph.m_taskDependOn[std::size_t(task)]) + bool const prevTriggered = rExec.m_taskTriggered.test(std::size_t(task)); + bool justTriggered = false; + bool dontEnqueueYet = false; + + for (DependOn const dependOn : graph.m_taskDependOn[std::size_t(task)]) { - if ( rExec.m_targetPendingCount[dependOn] != 0 - || rExec.m_targetWillBePending.test(std::size_t(dependOn)) ) + // If true: depend-on target is pending, wait for other tasks to finish + dontEnqueueYet |= rExec.m_targetPendingCount[dependOn.m_target] != 0; + + // If true: depend-on target is fulfilled by a task that will be enqueued, wait for it + dontEnqueueYet |= rExec.m_targetWillBePending.test(std::size_t(dependOn.m_target)); + + if ( ! prevTriggered && dependOn.m_target == calledFrom) { - return false; // Dependency target is or will be pending, wait for other tasks to finish + justTriggered = dependOn.m_trigger; } } - for (TargetId const dependOn : graph.m_taskDependOn[std::size_t(task)]) + // 'Triggered' indicates that one of the (trigger=true) depend-on targets became dirty for the + // period of time since this task last ran. + if (justTriggered) + { + rExec.m_taskTriggered.set(std::size_t(task)); + } + + if (dontEnqueueYet) + { + return false; + } + + // At least one of the trigger depend-on targets must have been dirty previously to enqueue + if ( ! (justTriggered || prevTriggered) ) { - ++ rExec.m_targetInUseCount[dependOn]; + return false; + } + + // Enqueue task + + for (DependOn const dependOn : graph.m_taskDependOn[std::size_t(task)]) + { + ++ rExec.m_targetInUseCount[dependOn.m_target]; } for (TargetId const fulfill : graph.m_taskFulfill[std::size_t(task)]) @@ -65,6 +94,8 @@ int enqueue_dirty(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec // Find out which targets will be pending for (std::size_t const target : rExec.m_targetDirty.ones()) { + rExec.m_targetDirty.set(target); + for (TaskId const dependent : graph.m_targetDependents[target]) { for (TargetId const fulfill : graph.m_taskFulfill[std::size_t(dependent)]) @@ -80,7 +111,7 @@ int enqueue_dirty(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec { for (TaskId const dependent : graph.m_targetDependents[target]) { - tasksEnqueued += int(try_enqueue_task(tasks, graph, rExec, dependent)); + tasksEnqueued += int(try_enqueue_task(tasks, graph, rExec, dependent, TargetId(target))); } } rExec.m_targetWillBePending.reset(); @@ -92,16 +123,13 @@ int enqueue_dirty(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec void mark_completed_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept { LGRN_ASSERTMV(rExec.m_tasksQueued.contains(task), - "Task must be queued to have been allowed to run", std::size_t(task)); + "Task must be queued to have been allowed to run", std::size_t(task)); - for (TargetId const dependOn : graph.m_taskDependOn[std::size_t(task)]) - { - -- rExec.m_targetInUseCount[dependOn]; + rExec.m_taskTriggered.reset(std::size_t(task)); - if (rExec.m_targetInUseCount[dependOn] == 0) - { - rExec.m_targetDirty.reset(std::size_t(dependOn)); - } + for (DependOn const dependOn : graph.m_taskDependOn[std::size_t(task)]) + { + -- rExec.m_targetInUseCount[dependOn.m_target]; } auto const taskFulfill = lgrn::Span(graph.m_taskFulfill[std::size_t(task)]); diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index f1911584..47de2888 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -47,19 +47,21 @@ struct ExecContext std::size_t const maxTasks = tasks.m_taskIds.capacity(); m_tasksQueued.reserve(maxTasks); + bitvector_resize(m_taskTriggered, maxTasks); - bitvector_resize(m_targetDirty, maxTargets); - bitvector_resize(m_targetWillBePending, maxTargets); + bitvector_resize(m_targetDirty, maxTargets); + bitvector_resize(m_targetWillBePending, maxTargets); m_targetPendingCount.resize(maxTargets); m_targetInUseCount .resize(maxTargets); } - entt::basic_sparse_set m_tasksQueued; + entt::basic_sparse_set m_tasksQueued; + BitVector_t m_taskTriggered; - BitVector_t m_targetDirty; - BitVector_t m_targetWillBePending; - KeyedVec m_targetPendingCount; - KeyedVec m_targetInUseCount; + BitVector_t m_targetDirty; + BitVector_t m_targetWillBePending; + KeyedVec m_targetPendingCount; + KeyedVec m_targetInUseCount; // TODO: Consider multithreading. something something work stealing... // * Allow multiple threads to search for and execute tasks. Atomic access diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index 101d5c03..8906e910 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -64,13 +64,13 @@ ExecGraph make_exec_graph(Tasks const& tasks, ArrayView totalDependEdges += pEdges->m_targetDependEdges.size(); totalFulfillEdges += pEdges->m_targetFulfillEdges.size(); - for (auto const [task, target] : pEdges->m_targetDependEdges) + for (auto const [task, target, _] : pEdges->m_targetDependEdges) { ++ taskCounts[task] .m_dependingOn; ++ targetCounts[target].m_dependents; } - for (auto const [task, target] : pEdges->m_targetFulfillEdges) + for (auto const [task, target, _] : pEdges->m_targetFulfillEdges) { ++ taskCounts[task] .m_fulfills; ++ targetCounts[target].m_fulfilledBy; @@ -123,19 +123,19 @@ ExecGraph make_exec_graph(Tasks const& tasks, ArrayView for (TaskEdges const* pEdges : data) { - for (auto const [task, target] : pEdges->m_targetDependEdges) + for (auto const [task, target, trigger] : pEdges->m_targetDependEdges) { - auto const dependOn = lgrn::Span (out.m_taskDependOn[TaskInt(task)]); + auto const dependOn = lgrn::Span (out.m_taskDependOn[TaskInt(task)]); auto const dependents = lgrn::Span (out.m_targetDependents[TargetInt(target)]); - dependOn [dependOn.size() - taskCounts[task].m_dependingOn] = target; + dependOn [dependOn.size() - taskCounts[task].m_dependingOn] = {target, trigger}; dependents [dependents.size() - targetCounts[target].m_dependents] = task; --taskCounts[task] .m_dependingOn; --targetCounts[target].m_dependents; } - for (auto const [task, target] : pEdges->m_targetFulfillEdges) + for (auto const [task, target, _] : pEdges->m_targetFulfillEdges) { auto const fulfills = lgrn::Span (out.m_taskFulfill[TaskInt(task)]); auto const fulfilledBy = lgrn::Span (out.m_targetFulfilledBy[TargetInt(target)]); @@ -152,7 +152,5 @@ ExecGraph make_exec_graph(Tasks const& tasks, ArrayView return out; } - - } // namespace osp diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index ea02ee71..3c5e5b7d 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -31,6 +31,7 @@ #include #include +#include #include namespace osp @@ -57,13 +58,14 @@ struct TaskEdges { struct TargetPair { - TaskId m_task; - TargetId m_target; + TaskId m_task; + TargetId m_target; + bool m_trigger; // unused for Fulfill edges }; struct SemaphorePair { - TaskId m_task; + TaskId m_task; SemaphoreId m_sema; }; @@ -72,9 +74,17 @@ struct TaskEdges std::vector m_semaphoreEdges; }; +struct DependOn +{ + TargetId m_target; + bool m_trigger; + + constexpr operator TargetId() const noexcept { return m_target; } +}; + struct ExecGraph { - lgrn::IntArrayMultiMap m_taskDependOn; /// Tasks depend on (n) Targets + lgrn::IntArrayMultiMap m_taskDependOn; /// Tasks depend on (n) Targets lgrn::IntArrayMultiMap m_targetDependents; /// Targets have (n) Tasks that depend on it lgrn::IntArrayMultiMap m_taskFulfill; /// Tasks fulfill (n) Targets @@ -97,5 +107,4 @@ inline ExecGraph make_exec_graph(Tasks const& tasks, std::initializer_list topData, ExecContext& rExec) +void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, WorkerContext worker) { std::vector topDataRefs; @@ -55,22 +55,42 @@ void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec TopTask &rTopTask = rTaskData[task]; + std::cout << "running: " << rTopTask.m_debugName << "\n"; + topDataRefs.clear(); topDataRefs.reserve(rTopTask.m_dataUsed.size()); for (TopDataId const dataId : rTopTask.m_dataUsed) { - topDataRefs.push_back((dataId != lgrn::id_null()) ? topData[dataId].as_ref() : entt::any{}); } - bool enqueueHappened = false; +// if (rTopTask.m_awareOfDirtyDeps) +// { +// worker.m_dependOnDirty.reset(); + +// std::size_t index = 0; +// for (TargetId const dependOn : graph.m_taskDependOn[std::size_t(task)]) +// { +// if (rExec.m_targetDirty.test(std::size_t(dependOn))) +// { +// worker.m_dependOnDirty.set(index); +// } +// ++index; +// } +// } // Task actually runs here. Results are not yet used for anything. - FulfillDirty_t const status = rTopTask.m_func(WorkerContext{}, topDataRefs); + FulfillDirty_t const status = rTopTask.m_func(worker, topDataRefs); mark_completed_task(tasks, graph, rExec, task, status); + + enqueue_dirty(tasks, graph, rExec); + for (TaskId const task : rExec.m_tasksQueued) + { + std::cout << "enq: " << rTaskData[task].m_debugName << "\n"; + } } } @@ -84,4 +104,42 @@ void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecContext& enqueue_dirty(tasks, graph, rExec); } + + +void write_dot_graph(std::ostream& rStream, Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData) +{ + rStream << "graph {\n" + << "rankdir=LR;\n" + << "nodesep=0.4;\n" + << "splines=line;\n"; + + // Define targets + rStream << "node [shape=circle,fixedsize=true]; "; + for (std::size_t const target : tasks.m_targetIds.bitview().zeros()) + { + rStream << target << ";"; + } + + rStream << "node [shape=rectangle,fixedsize=false];\n"; + + for (std::size_t const task : tasks.m_taskIds.bitview().zeros()) + { + rStream << "task_" << task << " [label=\"" << rTaskData.at(TaskId(task)).m_debugName << "\"];\n"; + + for (DependOn const dependOn : graph.m_taskDependOn[task]) + { + rStream << std::size_t(dependOn.m_target) << " -- task_" << task << " " + << (dependOn.m_trigger ? "[color=darkblue,penwidth=\"2px\", weight=3,]" : "[color=darkblue,style=dashed]") + << ";\n"; + } + + for (TargetId const fulfill : graph.m_taskFulfill[task]) + { + rStream << "task_" << task << " -- " << std::size_t(fulfill) << " [color=darkred];\n"; + } + } + + rStream << "}"; +} + } // namespace testapp diff --git a/src/osp/tasks/top_execute.h b/src/osp/tasks/top_execute.h index 7908e234..7b22a0cb 100644 --- a/src/osp/tasks/top_execute.h +++ b/src/osp/tasks/top_execute.h @@ -33,7 +33,7 @@ namespace osp { -void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec); +void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, WorkerContext worker = {}); void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, ArrayView enqueue); @@ -42,4 +42,6 @@ inline void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecCo return top_enqueue_quick(tasks, graph, rExec, Corrade::Containers::arrayView(enqueue)); } +void write_dot_graph(std::ostream& rStream, Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData); + } // namespace testapp diff --git a/src/osp/tasks/top_session.cpp b/src/osp/tasks/top_session.cpp index 1c535f84..7010b4b3 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/osp/tasks/top_session.cpp @@ -47,7 +47,7 @@ void top_close_session( // Run cleanup tasks for (Session &rSession : sessions) { - if (TargetId const cleanup = std::exchange(rSession.m_cleanupTgt, lgrn::id_null()); + if (TargetId const cleanup = std::exchange(rSession.m_cleanup, lgrn::id_null()); cleanup != lgrn::id_null()) { rExec.m_targetDirty.set(std::size_t(cleanup)); diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index bf1db8cb..d0c48c67 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -75,7 +75,7 @@ namespace osp struct Session { template - std::array acquire_data(ArrayView topData) + [[nodiscard]] std::array acquire_data(ArrayView topData) { std::array out; top_reserve(topData, 0, std::begin(out), std::end(out)); @@ -101,7 +101,7 @@ struct Session } template - TGT_STRUCT_T get_targets() const + [[nodiscard]] TGT_STRUCT_T get_targets() const { static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); @@ -116,16 +116,11 @@ struct Session return reinterpret_cast(*m_targets.data()); } - TaskId& task() - { - return m_tasks.emplace_back(lgrn::id_null()); - } - std::vector m_data; std::vector m_targets; std::vector m_tasks; - TargetId m_cleanupTgt{lgrn::id_null()}; + TargetId m_cleanup {lgrn::id_null()}; std::size_t m_targetStructHash{0}; std::string m_targetStructName; diff --git a/src/osp/tasks/top_tasks.h b/src/osp/tasks/top_tasks.h index c97d6938..734d2700 100644 --- a/src/osp/tasks/top_tasks.h +++ b/src/osp/tasks/top_tasks.h @@ -39,7 +39,8 @@ struct TopTask { std::string m_debugName; std::vector m_dataUsed; - TopTaskFunc_t m_func{nullptr}; + TopTaskFunc_t m_func { nullptr }; + //bool m_awareOfDirtyDeps { false }; }; using TopTaskDataVec_t = KeyedVec; diff --git a/src/osp/tasks/top_utils.h b/src/osp/tasks/top_utils.h index 2345affb..e3fc5367 100644 --- a/src/osp/tasks/top_utils.h +++ b/src/osp/tasks/top_utils.h @@ -116,6 +116,7 @@ struct wrap_args_trait } else { + LGRN_ASSERTMV(topData.size() > argIndex, "Task function has more arguments than TopDataIds provided", topData.size(), argIndex); return entt::any_cast(topData[argIndex]); } } @@ -123,7 +124,6 @@ struct wrap_args_trait template static constexpr decltype(auto) cast_args(ArrayView topData, WorkerContext ctx, [[maybe_unused]] std::index_sequence indices) noexcept { - assert(topData.size() == sizeof...(ARGS_T)); return FUNCTOR_T{}(cast_arg(topData, ctx, INDEX_T) ...); } @@ -133,7 +133,7 @@ struct wrap_args_trait if constexpr (std::is_void_v) { cast_args(topData, ctx, std::make_index_sequence{}); - return {{0xFFFFFFFFFFFFFFFFul}}; // All fulfilled targets set dirty + return gc_fulfillAll; // All fulfilled targets set dirty } else if constexpr (std::is_same_v) { @@ -201,12 +201,14 @@ struct TopTaskBuilder : public TaskBuilderBase struct TopTaskRef : public TaskRefBase { inline TopTaskRef& name(std::string_view debugName); - inline TopTaskRef& data(std::initializer_list dataUsed); + inline TopTaskRef& args(std::initializer_list dataUsed); template - TopTaskRef& func(FUNC_T funcArg); + TopTaskRef& func(FUNC_T&& funcArg); inline TopTaskRef& func_raw(TopTaskFunc_t func); + inline TopTaskRef& important_deps_count(int value); + template TopTaskRef& push_to(CONTAINER_T& rContainer); }; @@ -218,7 +220,7 @@ TopTaskRef& TopTaskRef::name(std::string_view debugName) return *this; } -TopTaskRef& TopTaskRef::data(std::initializer_list dataUsed) +TopTaskRef& TopTaskRef::args(std::initializer_list dataUsed) { m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_dataUsed = dataUsed; @@ -226,7 +228,7 @@ TopTaskRef& TopTaskRef::data(std::initializer_list dataUsed) } template -TopTaskRef& TopTaskRef::func(FUNC_T funcArg) +TopTaskRef& TopTaskRef::func(FUNC_T&& funcArg) { m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_func = wrap_args(funcArg); @@ -240,6 +242,13 @@ TopTaskRef& TopTaskRef::func_raw(TopTaskFunc_t func) return *this; } +//TopTaskRef& TopTaskRef::aware_of_dirty_depends(bool value) +//{ +// m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); +// m_rBuilder.m_rData[m_taskId].m_awareOfDirtyDeps = value; +// return *this; +//} + template TopTaskRef& TopTaskRef::push_to(CONTAINER_T& rContainer) { diff --git a/src/osp/tasks/top_worker.h b/src/osp/tasks/top_worker.h index 33d27fb3..264e9354 100644 --- a/src/osp/tasks/top_worker.h +++ b/src/osp/tasks/top_worker.h @@ -36,8 +36,23 @@ namespace osp { +/** + * @brief Return value from a task to tell the executor which fulfilled targets are dirty + * + * Bits here limits the max number of fulfill targets per-task to 1*64 + */ using FulfillDirty_t = lgrn::BitView>; +/** + * @brief Passed to tasks to signify which targets the task depends on are dirty + * + * Bits here limits the max dependencies per-task to 4*64 + */ +//using DependOnDirty_t = lgrn::BitView>; + +constexpr FulfillDirty_t gc_fulfillAll{{0xFFFFFFFFFFFFFFFFul}}; +constexpr FulfillDirty_t gc_fulfillNone{{0}}; + using TopDataId = uint32_t; using TopDataIds_t = std::initializer_list; @@ -45,7 +60,7 @@ struct Reserved {}; struct WorkerContext { - + //DependOnDirty_t m_dependOnDirty; }; using TopTaskFunc_t = FulfillDirty_t(*)(WorkerContext, ArrayView) noexcept; diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 7934fc86..33b766f3 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -29,41 +29,29 @@ namespace testapp { -// Identifiers made for OSP_ACQUIRE_* and OSP_UNPACK_* macros -// Used to declare variable names for TopDataIds and TagIds -// #define OSP_[DATA/TAGS]_NAME <# of identifiers>, a, b, c, d, ... - -// Tag naming: -// -// tg...Evt: Event, tasks with these tags are enqueued externally -// tg...New: Adds new instances -// tg...Del: Deletes instances -// tg...Mod: Modifies some data -// tg...Req: Requires some data after its modified -// tg...Prv: Requires previous from last update -// tg...Clr: Clears a queue after its used -// -// * Tasks with a tg...Req tag will run AFTER its corresponding tg...Mod tag -// * New often depends on Delete, as deleting instances first leaves empty -// spaces in a container, best immediately filled with new instances +// Notes: +// * "Delete" tasks often run before "New" tasks, since deletion leaves empty spaces in arrays +// that are best populated with new instances right away. +// * "Delete" task fulfills _del and _mod +// * "New" task depends on _del, fulfills _mod and _new // // Scene sessions -#define TESTAPP_DATA_COMMON_SCENE 9, \ - idDeltaTimeIn, idActiveIds, idBasic, idDrawing, idDrawingRes, idDelEnts, idDelTotal, idDelDrawEnts, idNMesh -#define OSP_TAGS_TESTAPP_COMMON_SCENE 37, \ - tgCleanupEvt, tgResyncEvt, tgSyncEvt, tgSceneEvt, tgTimeEvt, \ - tgEntDel, tgEntNew, tgEntReq, \ - tgDelEntMod, tgDelEntReq, tgDelEntClr, \ - tgDelTotalMod, tgDelTotalReq, tgDelTotalClr, \ - tgTransformMod, tgTransformDel, tgTransformNew, tgTransformReq, \ - tgHierMod, tgHierModEnd, tgHierDel, tgHierNew, tgHierReq, \ - tgDrawDel, tgDrawMod, tgDrawReq, \ - tgDelDrawEntMod, tgDelDrawEntReq, tgDelDrawEntClr, \ - tgMeshDel, tgMeshMod, tgMeshReq, tgMeshClr, \ - tgTexDel, tgTexMod, tgTexReq, tgTexClr +// Persistent: +// +// DeleteTask --->(_del)---> NewTask --->(_mod)---> UseTask --->(_use) +// | ^ +// +-----------------------------------+ +// + +// Transient: containers filled and cleared right away +// +// PushValuesTask --->(_mod)---> UseValuesTask --->(_use)---> ClearValuesTask --->(_clr) +// +#define TESTAPP_DATA_SCENE 1, \ + idDeltaTimeIn struct TgtScene { osp::TargetId cleanup; @@ -72,39 +60,45 @@ struct TgtScene osp::TargetId resyncAll; }; -#define TESTAPP_DATA_MATERIAL 2, \ - idMatEnts, idMatDirty -#define OSP_TAGS_TESTAPP_MATERIAL 4, \ - tgMatDel, tgMatMod, tgMatReq, tgMatClr - - - -#define TESTAPP_DATA_MATERIAL 2, \ - idMatEnts, idMatDirty -#define OSP_TAGS_TESTAPP_MATERIAL 4, \ - tgMatDel, tgMatMod, tgMatReq, tgMatClr - +#define TESTAPP_DATA_COMMON_SCENE 6, \ + idBasic, idDrawing, idDrawingRes, idActiveEntDel, idDrawEntDel, idNMesh +struct TgtCommonScene +{ + osp::TargetId activeEnt_del, activeEnt_new, activeEnt_mod, activeEnt_use; + osp::TargetId delActiveEnt_mod, delActiveEnt_use, delActiveEnt_clr; + osp::TargetId transform_del, transform_new, transform_mod, transform_use; + osp::TargetId hier_del, hier_new, hier_mod, hier_use; + osp::TargetId drawEnt_del, drawEnt_new, drawEnt_mod, drawEnt_use; + osp::TargetId delDrawEnt_mod, delDrawEnt_use, delDrawEnt_clr; + osp::TargetId mesh_del, mesh_new, mesh_mod, mesh_use; + osp::TargetId texture_del, texture_new, texture_mod, texture_use; + osp::TargetId material_del, material_new, material_mod, material_use, material_clr; +}; #define TESTAPP_DATA_PHYSICS 3, \ idPhys, idHierBody, idPhysIn -#define OSP_TAGS_TESTAPP_PHYSICS 6, \ - tgPhysPrv, tgPhysDel, tgPhysMod, tgPhysReq, \ - tgPhysTransformMod, tgPhysTransformReq +struct TgtPhysics +{ + osp::TargetId physics_del, physics_new, physics_mod, physics_use; +}; -#define TESTAPP_DATA_SHAPE_SPAWN 2, \ - idSpawner, idSpawnerEnts -#define OSP_TAGS_TESTAPP_SHAPE_SPAWN 5, \ - tgSpawnMod, tgSpawnReq, tgSpawnClr, \ - tgSpawnEntMod, tgSpawnEntReq + +#define TESTAPP_DATA_SHAPE_SPAWN 1, \ + idSpawner +struct TgtShapeSpawn +{ + osp::TargetId spawnRequest_mod, spawnRequest_use, spawnRequest_clr; + osp::TargetId spawnedEnts_mod, spawnedEnts_use; // spawnRequest_clr; +}; #define TESTAPP_DATA_PREFABS 1, \ idPrefabInit -#define OSP_TAGS_TESTAPP_PREFABS 7, \ +#define OS, tP_TAGS_TESTAPP_PREFABS 7, \ tgPrefabMod, tgPrefabReq, tgPrefabClr, \ tgPrefabEntMod, tgPrefabEntReq, \ tgPfParentHierMod, tgPfParentHierReq @@ -176,9 +170,10 @@ struct TgtScene #define TESTAPP_DATA_NEWTON 1, \ idNwt -#define OSP_TAGS_TESTAPP_NEWTON 5, \ - tgNwtBodyPrv, tgNwtBodyDel, tgNwtBodyMod, tgNwtBodyReq, tgNwtBodyClr - +struct TgtNewton +{ + osp::TargetId nwtBody_del, nwtBody_new, nwtBody_mod, nwtBody_use; +}; #define TESTAPP_DATA_NEWTON_FORCES 1, \ idNwtFactors @@ -224,7 +219,6 @@ struct TgtScene #define TESTAPP_DATA_WINDOW_APP 1, \ idUserInput - struct TgtWindowApp { osp::TargetId input; @@ -235,34 +229,40 @@ struct TgtWindowApp #define TESTAPP_DATA_MAGNUM 2, \ idActiveApp, idRenderGl - struct TgtMagnum { osp::TargetId cleanup; + + osp::TargetId meshGL_mod, meshGL_use; + osp::TargetId textureGL_mod, textureGL_use; }; + #define TESTAPP_DATA_COMMON_RENDERER 3, \ idScnRender, idGroupFwd, idCamera -#define OSP_TAGS_TESTAPP_COMMON_RENDERER 24, \ - tgDrawGlDel, tgDrawGlMod, tgDrawGlReq, \ - tgMeshGlMod, tgMeshGlReq, \ - tgTexGlMod, tgTexGlReq, \ - tgEntTexMod, tgEntTexReq, \ - tgEntMeshMod, tgEntMeshReq, \ - tgCameraMod, tgCameraReq, \ - tgGroupFwdDel, tgGroupFwdMod, tgGroupFwdReq, \ - tgBindFboMod, tgBindFboReq, \ - tgFwdRenderMod, tgFwdRenderReq, \ - tgDrawTransformDel, tgDrawTransformNew, tgDrawTransformMod, tgDrawTransformReq +struct TgtSceneRenderer +{ + osp::TargetId fboRender; + osp::TargetId fboRenderDone; + + osp::TargetId scnRender_del, scnRender_new, scnRender_mod, scnRender_use; + osp::TargetId group_mod, group_use; + osp::TargetId groupEnts_del, groupEnts_new, groupEnts_mod, groupEnts_use; + osp::TargetId drawTransform_new, drawTransform_mod, drawTransform_use; + osp::TargetId camera_mod, camera_use; + osp::TargetId entMesh_new, entMesh_mod, entMesh_use; + osp::TargetId entTexture_new, entTexture_mod, entTexture_use; +}; #define TESTAPP_DATA_CAMERA_CTRL 1, \ idCamCtrl -#define OSP_TAGS_TESTAPP_CAMERA_CTRL 2, \ - tgCamCtrlMod, tgCamCtrlReq - +struct TgtCameraCtrl +{ + osp::TargetId cameraCtrl_mod, cameraCtrl_use; +}; #define TESTAPP_DATA_SHADER_VISUALIZER 1, \ @@ -290,4 +290,4 @@ struct TgtMagnum #define OSP_TAGS_TESTAPP_VEHICLE_CONTROL 2, \ tgSelUsrCtrlMod, tgSelUsrCtrlReq -} // namespace testapp::targets +} // namespace testapp diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 0bffef0e..c4ed321e 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -56,34 +56,47 @@ using namespace osp; namespace testapp { -static void setup_magnum_draw(Session const& windowApp, Session const& magnum, Session const& scene, Session const& scnRender, std::vector run = {}) +static constexpr auto sc_matVisualizer = active::MaterialId(0); +static constexpr auto sc_matFlat = active::MaterialId(1); +static constexpr auto sc_matPhong = active::MaterialId(2); +static constexpr int sc_materialCount = 4; + + +static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& scnRenderer, std::vector run = {}) { -#if 0 - OSP_DECLARE_GET_DATA_IDS(scnRender, TESTAPP_DATA_COMMON_RENDERER); - OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); - OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); + OSP_DECLARE_GET_DATA_IDS(scnRenderer, TESTAPP_DATA_COMMON_RENDERER); + OSP_DECLARE_GET_DATA_IDS(rTestApp.m_windowApp, TESTAPP_DATA_WINDOW_APP); + OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); - auto &rActiveApp = top_get(main.m_topData, idActiveApp); - auto &rCamera = top_get(main.m_topData, idCamera); + auto &rActiveApp = top_get(rTestApp.m_topData, idActiveApp); + auto &rCamera = top_get(rTestApp.m_topData, idCamera); rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); - auto tgScn = scene .get_targets(); - auto tgApp = windowApp .get_targets(); + auto tgScn = scene .get_targets(); + auto tgWin = rTestApp.m_windowApp .get_targets(); // Run Resync tasks to mark all used gpu resources as dirty - top_enqueue_quick(main.m_rTasks, main.m_rGraph, main.m_rExec, {tgScn.resyncAll}); - top_run_blocking(main.m_rTasks, main.m_rGraph, main.m_rTaskData, main.m_topData, main.m_rExec); + auto aaa = scene.get_targets(); + rTestApp.m_exec.m_targetDirty.set(std::size_t(aaa.resyncAll)); - run.insert(run.end(), {tgScn.sync, tgScn.time, tgApp.input, tgApp.render}); + run.insert(run.end(), {tgScn.sync, tgScn.time, tgWin.input, tgWin.render}); // run gets copied but who cares lol - rActiveApp.set_on_draw( [main, runTagsVec = std::move(run)] + rActiveApp.set_on_draw( [&rTestApp, runTagsVec = std::move(run), resync =tgScn.resyncAll] (ActiveApplication& rApp, float delta) { // Magnum Application's main loop is here - top_enqueue_quick(main.m_rTasks, main.m_rGraph, main.m_rExec, runTagsVec); - top_run_blocking(main.m_rTasks, main.m_rGraph, main.m_rTaskData, main.m_topData, main.m_rExec); + std::cout << "\n---- START ----\n"; + + top_enqueue_quick(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_exec, runTagsVec); + + for (TaskId const task : rTestApp.m_exec.m_tasksQueued) + { + std::cout << "enq: " << rTestApp.m_taskData[task].m_debugName << "\n"; + } + + top_run_blocking(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec); // Enqueued tasks that don't run indicate a deadlock // if ( ! std::all_of(std::begin(rExec.m_taskQueuedCounts), @@ -95,11 +108,10 @@ static void setup_magnum_draw(Session const& windowApp, Session const& magnum, S // std::abort(); // } }); -#endif } static ScenarioMap_t make_scenarios() -{ +{ ScenarioMap_t scenarioMap; auto const add_scenario = [&scenarioMap] (std::string_view name, std::string_view desc, SceneSetupFunc_t run) @@ -135,54 +147,73 @@ static ScenarioMap_t make_scenarios() }; }); -#if 0 - add_scenario("physics", "Newton Dynamics integration test scenario", - [] (MainView mainView, Sessions_t& sceneOut) -> RendererSetup_t + [] (TestApp& rTestApp) -> RendererSetupFunc_t { using namespace testapp::scenes; - auto const idResources = mainView.m_idResources; - auto &rTopData = mainView.m_topData; - auto &rTags = mainView.m_rTags; - Builder_t builder{rTags, mainView.m_rTasks, mainView.m_rTaskData}; + auto const defaultPkg = rTestApp.m_defaultPkg; + auto const idResources = rTestApp.m_idResources; + auto & rTopData = rTestApp.m_topData; + + TopTaskBuilder builder{rTestApp.m_tasks, rTestApp.m_scene.m_edges, rTestApp.m_taskData}; - auto & [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt] = resize_then_unpack<10>(sceneOut); + auto & [ + scene, commonScene, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt + ] = resize_then_unpack<10>(rTestApp.m_scene.m_sessions); // Compose together lots of Sessions - scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); - matVisual = setup_material (builder, rTopData, rTags, scnCommon); - physics = setup_physics (builder, rTopData, rTags, scnCommon); - shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, scnCommon, physics, matVisual); - droppers = setup_droppers (builder, rTopData, rTags, scnCommon, shapeSpawn); - bounds = setup_bounds (builder, rTopData, rTags, scnCommon, physics, shapeSpawn); - - newton = setup_newton (builder, rTopData, rTags, scnCommon, physics); - nwtGravSet = setup_newton_factors (builder, rTopData, rTags); - nwtGrav = setup_newton_force_accel (builder, rTopData, rTags, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); - shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, rTags, scnCommon, physics, shapeSpawn, newton, nwtGravSet); + scene = setup_scene (builder, rTopData); + commonScene = setup_common_scene (builder, rTopData, scene, idResources, defaultPkg); + physics = setup_physics (builder, rTopData, commonScene); + shapeSpawn = setup_shape_spawn (builder, rTopData, commonScene, physics, sc_matVisualizer); + //droppers = setup_droppers (builder, rTopData, commonScene, shapeSpawn); + //bounds = setup_bounds (builder, rTopData, commonScene, physics, shapeSpawn); - add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, mainView.m_defaultPkg); + newton = setup_newton (builder, rTopData, scene, commonScene, physics); + nwtGravSet = setup_newton_factors (builder, rTopData); + //nwtGrav = setup_newton_force_accel (builder, rTopData, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); + shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, commonScene, physics, shapeSpawn, newton, nwtGravSet); - return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, [[maybe_unused]] Sessions_t& rendererOut) + create_materials(rTopData, commonScene, sc_materialCount); + add_floor(rTopData, commonScene, shapeSpawn, sc_matVisualizer, idResources, defaultPkg); + + rTestApp.m_exec.resize(rTestApp.m_tasks); + auto bbb = commonScene.get_targets(); + rTestApp.m_exec.m_targetDirty.set(std::size_t(commonScene.get_targets().drawEnt_mod)); + rTestApp.m_exec.m_targetDirty.set(std::size_t(shapeSpawn.get_targets().spawnRequest_mod)); + + + return [] (TestApp& rTestApp) { - auto &rTopData = mainView.m_topData; - auto &rTags = mainView.m_rTags; - Builder_t builder{mainView.m_rTags, mainView.m_rTasks, mainView.m_rTaskData}; + auto const windowApp = rTestApp.m_windowApp; + auto const magnum = rTestApp.m_magnum; + auto const defaultPkg = rTestApp.m_defaultPkg; + auto const idResources = rTestApp.m_idResources; + auto & rTopData = rTestApp.m_topData; - auto const& [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt] = unpack<10>(scene); + TopTaskBuilder builder{rTestApp.m_tasks, rTestApp.m_renderer.m_edges, rTestApp.m_taskData}; - auto & [scnRender, cameraCtrl, cameraFree, shVisual, camThrow] = resize_then_unpack<5>(rendererOut); - scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); - cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); - cameraFree = setup_camera_free (builder, rTopData, rTags, magnum, scnCommon, cameraCtrl); - shVisual = setup_shader_visualizer (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); - camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); + auto & [ + scene, commonScene, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt + ] = unpack<10>(rTestApp.m_scene.m_sessions); - setup_magnum_draw(mainView, magnum, scnCommon, scnRender); + auto & [ + scnRender, cameraCtrl, cameraFree, shVisual, camThrow + ] = resize_then_unpack<5>(rTestApp.m_renderer.m_sessions); + + scnRender = setup_scene_renderer (builder, rTopData, windowApp, magnum, scene, commonScene, idResources); + cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, scnRender); + cameraFree = setup_camera_free (builder, rTopData, windowApp, scene, cameraCtrl); + shVisual = setup_shader_visualizer (builder, rTopData, magnum, commonScene, scnRender, sc_matVisualizer); + camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); + + setup_magnum_draw(rTestApp, scene, scnRender); }; }); +#if 0 + add_scenario("vehicles", "Physics scenario but with Vehicles", [] (MainView mainView, Sessions_t& sceneOut) -> RendererSetup_t { @@ -196,7 +227,7 @@ static ScenarioMap_t make_scenarios() auto & [ - scnCommon, matVisual, physics, shapeSpawn, + commonScene, matVisual, physics, shapeSpawn, prefabs, parts, vehicleSpawn, vehicleSpawnVB, vehicleSpawnRgd, signalsFloat, machRocket, machRcsDriver, @@ -204,35 +235,35 @@ static ScenarioMap_t make_scenarios() newton, nwtGravSet, nwtGrav, shapeSpawnNwt, vehicleSpawnNwt, nwtRocketSet, rocketsNwt ] = resize_then_unpack<24>(sceneOut); - scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); - matVisual = setup_material (builder, rTopData, rTags, scnCommon); - physics = setup_physics (builder, rTopData, rTags, scnCommon); - shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, scnCommon, physics, matVisual); - prefabs = setup_prefabs (builder, rTopData, rTags, scnCommon, physics, matVisual, idResources); - parts = setup_parts (builder, rTopData, rTags, scnCommon, idResources); - signalsFloat = setup_signals_float (builder, rTopData, rTags, scnCommon, parts); - vehicleSpawn = setup_vehicle_spawn (builder, rTopData, rTags, scnCommon); - vehicleSpawnVB = setup_vehicle_spawn_vb (builder, rTopData, rTags, scnCommon, prefabs, parts, vehicleSpawn, signalsFloat, idResources); - machRocket = setup_mach_rocket (builder, rTopData, rTags, scnCommon, parts, signalsFloat); - machRcsDriver = setup_mach_rcsdriver (builder, rTopData, rTags, scnCommon, parts, signalsFloat); - testVehicles = setup_test_vehicles (builder, rTopData, rTags, scnCommon, idResources); - droppers = setup_droppers (builder, rTopData, rTags, scnCommon, shapeSpawn); - bounds = setup_bounds (builder, rTopData, rTags, scnCommon, physics, shapeSpawn); - - newton = setup_newton (builder, rTopData, rTags, scnCommon, physics); + commonScene = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); + matVisual = setup_material (builder, rTopData, rTags, commonScene); + physics = setup_physics (builder, rTopData, rTags, commonScene); + shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, commonScene, physics, matVisual); + prefabs = setup_prefabs (builder, rTopData, rTags, commonScene, physics, matVisual, idResources); + parts = setup_parts (builder, rTopData, rTags, commonScene, idResources); + signalsFloat = setup_signals_float (builder, rTopData, rTags, commonScene, parts); + vehicleSpawn = setup_vehicle_spawn (builder, rTopData, rTags, commonScene); + vehicleSpawnVB = setup_vehicle_spawn_vb (builder, rTopData, rTags, commonScene, prefabs, parts, vehicleSpawn, signalsFloat, idResources); + machRocket = setup_mach_rocket (builder, rTopData, rTags, commonScene, parts, signalsFloat); + machRcsDriver = setup_mach_rcsdriver (builder, rTopData, rTags, commonScene, parts, signalsFloat); + testVehicles = setup_test_vehicles (builder, rTopData, rTags, commonScene, idResources); + droppers = setup_droppers (builder, rTopData, rTags, commonScene, shapeSpawn); + bounds = setup_bounds (builder, rTopData, rTags, commonScene, physics, shapeSpawn); + + newton = setup_newton (builder, rTopData, rTags, commonScene, physics); nwtGravSet = setup_newton_factors (builder, rTopData, rTags); nwtGrav = setup_newton_force_accel (builder, rTopData, rTags, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); - shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, rTags, scnCommon, physics, shapeSpawn, newton, nwtGravSet); - vehicleSpawnNwt = setup_vehicle_spawn_newton(builder, rTopData, rTags, scnCommon, physics, prefabs, parts, vehicleSpawn, newton, idResources); + shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, rTags, commonScene, physics, shapeSpawn, newton, nwtGravSet); + vehicleSpawnNwt = setup_vehicle_spawn_newton(builder, rTopData, rTags, commonScene, physics, prefabs, parts, vehicleSpawn, newton, idResources); nwtRocketSet = setup_newton_factors (builder, rTopData, rTags); - rocketsNwt = setup_rocket_thrust_newton(builder, rTopData, rTags, scnCommon, physics, prefabs, parts, signalsFloat, newton, nwtRocketSet); + rocketsNwt = setup_rocket_thrust_newton(builder, rTopData, rTags, commonScene, physics, prefabs, parts, signalsFloat, newton, nwtRocketSet); - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(vehicleSpawn, TESTAPP_VEHICLE_SPAWN); OSP_SESSION_UNPACK_DATA(vehicleSpawnVB, TESTAPP_VEHICLE_SPAWN_VB); OSP_SESSION_UNPACK_DATA(testVehicles, TESTAPP_TEST_VEHICLES); - add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, mainView.m_defaultPkg); + add_floor(rTopData, commonScene, matVisual, shapeSpawn, idResources, mainView.m_defaultPkg); auto &rActiveIds = top_get (rTopData, idActiveIds); auto &rTVPartVehicle = top_get (rTopData, idTVPartVehicle); @@ -258,7 +289,7 @@ static ScenarioMap_t make_scenarios() auto const& [ - scnCommon, matVisual, physics, shapeSpawn, + commonScene, matVisual, physics, shapeSpawn, prefabs, parts, vehicleSpawn, vehicleSpawnVB, vehicleSpawnRgd, signalsFloat, machRocket, machRcsDriver, @@ -267,16 +298,16 @@ static ScenarioMap_t make_scenarios() ] = unpack<24>(scene); auto & [scnRender, cameraCtrl, shPhong, shFlat, camThrow, vehicleCtrl, cameraVehicle, thrustIndicator] = resize_then_unpack<8>(rendererOut); - scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); + scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, commonScene, mainView.m_idResources); cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); - shPhong = setup_shader_phong (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); - shFlat = setup_shader_flat (builder, rTopData, rTags, magnum, scnCommon, scnRender, {}); + shPhong = setup_shader_phong (builder, rTopData, rTags, magnum, commonScene, scnRender, matVisual); + shFlat = setup_shader_flat (builder, rTopData, rTags, magnum, commonScene, scnRender, {}); camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); - vehicleCtrl = setup_vehicle_control (builder, rTopData, rTags, scnCommon, parts, signalsFloat, magnum); - cameraVehicle = setup_camera_vehicle (builder, rTopData, rTags, magnum, scnCommon, parts, physics, cameraCtrl, vehicleCtrl); - thrustIndicator = setup_thrust_indicators (builder, rTopData, rTags, magnum, scnCommon, parts, signalsFloat, scnRender, cameraCtrl, shFlat, mainView.m_idResources, mainView.m_defaultPkg); + vehicleCtrl = setup_vehicle_control (builder, rTopData, rTags, commonScene, parts, signalsFloat, magnum); + cameraVehicle = setup_camera_vehicle (builder, rTopData, rTags, magnum, commonScene, parts, physics, cameraCtrl, vehicleCtrl); + thrustIndicator = setup_thrust_indicators (builder, rTopData, rTags, magnum, commonScene, parts, signalsFloat, scnRender, cameraCtrl, shFlat, mainView.m_idResources, mainView.m_defaultPkg); - setup_magnum_draw(mainView, magnum, scnCommon, scnRender); + setup_magnum_draw(mainView, magnum, commonScene, scnRender); }; }); @@ -292,27 +323,27 @@ static ScenarioMap_t make_scenarios() auto & [ - scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets + commonScene, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets ] = resize_then_unpack<13>(sceneOut); // Compose together lots of Sessions - scnCommon = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); - matVisual = setup_material (builder, rTopData, rTags, scnCommon); - physics = setup_physics (builder, rTopData, rTags, scnCommon); - shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, scnCommon, physics, matVisual); - droppers = setup_droppers (builder, rTopData, rTags, scnCommon, shapeSpawn); - bounds = setup_bounds (builder, rTopData, rTags, scnCommon, physics, shapeSpawn); - - newton = setup_newton (builder, rTopData, rTags, scnCommon, physics); + commonScene = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); + matVisual = setup_material (builder, rTopData, rTags, commonScene); + physics = setup_physics (builder, rTopData, rTags, commonScene); + shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, commonScene, physics, matVisual); + droppers = setup_droppers (builder, rTopData, rTags, commonScene, shapeSpawn); + bounds = setup_bounds (builder, rTopData, rTags, commonScene, physics, shapeSpawn); + + newton = setup_newton (builder, rTopData, rTags, commonScene, physics); nwtGravSet = setup_newton_factors (builder, rTopData, rTags); nwtGrav = setup_newton_force_accel (builder, rTopData, rTags, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); - shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, rTags, scnCommon, physics, shapeSpawn, newton, nwtGravSet); + shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, rTags, commonScene, physics, shapeSpawn, newton, nwtGravSet); uniCore = setup_uni_core (builder, rTopData, rTags); uniScnFrame = setup_uni_sceneframe (builder, rTopData, rTags); uniTestPlanets = setup_uni_test_planets (builder, rTopData, rTags, uniCore, uniScnFrame); - add_floor(rTopData, scnCommon, matVisual, shapeSpawn, idResources, mainView.m_defaultPkg); + add_floor(rTopData, commonScene, matVisual, shapeSpawn, idResources, mainView.m_defaultPkg); return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, Sessions_t& rendererOut) { @@ -320,22 +351,22 @@ static ScenarioMap_t make_scenarios() auto &rTags = mainView.m_rTags; Builder_t builder{mainView.m_rTags, mainView.m_rTasks, mainView.m_rTaskData}; - auto const& [scnCommon, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(scene); + auto const& [commonScene, matVisual, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets] = unpack<13>(scene); rendererOut.resize(8); auto & [scnRender, cameraCtrl, cameraFree, shFlat, shVisual, camThrow, cursor, uniTestPlanetsRdr] = unpack<8>(rendererOut); - scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, scnCommon, mainView.m_idResources); + scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, commonScene, mainView.m_idResources); cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); - cameraFree = setup_camera_free (builder, rTopData, rTags, magnum, scnCommon, cameraCtrl); - shFlat = setup_shader_flat (builder, rTopData, rTags, magnum, scnCommon, scnRender, {}); - shVisual = setup_shader_visualizer (builder, rTopData, rTags, magnum, scnCommon, scnRender, matVisual); + cameraFree = setup_camera_free (builder, rTopData, rTags, magnum, commonScene, cameraCtrl); + shFlat = setup_shader_flat (builder, rTopData, rTags, magnum, commonScene, scnRender, {}); + shVisual = setup_shader_visualizer (builder, rTopData, rTags, magnum, commonScene, scnRender, matVisual); camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); - cursor = setup_cursor (builder, rTopData, rTags, magnum, scnCommon, scnRender, cameraCtrl, shFlat, mainView.m_idResources, mainView.m_defaultPkg); - uniTestPlanetsRdr = setup_uni_test_planets_renderer (builder, rTopData, rTags, magnum, scnRender, scnCommon, cameraCtrl, shVisual, uniCore, uniScnFrame, uniTestPlanets); + cursor = setup_cursor (builder, rTopData, rTags, magnum, commonScene, scnRender, cameraCtrl, shFlat, mainView.m_idResources, mainView.m_defaultPkg); + uniTestPlanetsRdr = setup_uni_test_planets_renderer (builder, rTopData, rTags, magnum, scnRender, commonScene, cameraCtrl, shVisual, uniCore, uniScnFrame, uniTestPlanets); OSP_SESSION_UNPACK_TAGS(uniCore, TESTAPP_UNI_CORE); - setup_magnum_draw(mainView, magnum, scnCommon, scnRender, {tgUniTimeEvt}); + setup_magnum_draw(mainView, magnum, commonScene, scnRender, {tgUniTimeEvt}); }; }); diff --git a/src/test_application/activescenes/scenarios_enginetest.cpp b/src/test_application/activescenes/scenarios_enginetest.cpp index 9d8f7584..8bff8518 100644 --- a/src/test_application/activescenes/scenarios_enginetest.cpp +++ b/src/test_application/activescenes/scenarios_enginetest.cpp @@ -96,7 +96,7 @@ struct EngineTestScene ActiveEnt m_cube{lgrn::id_null()}; // Set of ActiveEnts that are assigned a Phong material - osp::active::EntSet_t m_matPhong; + osp::active::ActiveEntSet_t m_matPhong; std::vector m_matPhongDirty; }; diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index d51c54f5..a3315baa 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -42,183 +42,187 @@ using namespace osp::active; namespace testapp::scenes { -#if 0 +Session setup_scene( + TopTaskBuilder& rBuilder, + ArrayView const topData) +{ + osp::Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_SCENE); + + top_emplace< float >(topData, idDeltaTimeIn, 1.0f / 60.0f); + + out.create_targets(rBuilder); + return out; +} Session setup_common_scene( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& scene, TopDataId const idResources, PkgId const pkg) { + auto const tgScn = scene.get_targets(); auto &rResources = top_get< Resources > (topData, idResources); - Session scnCommon; - OSP_SESSION_ACQUIRE_DATA(scnCommon, topData, TESTAPP_COMMON_SCENE); - - - top_emplace< float > (topData, idDeltaTimeIn, 1.0f / 60.0f); - top_emplace< EntVector_t > (topData, idDelEnts); - top_emplace< EntVector_t > (topData, idDelTotal); - top_emplace< DrawEntVector_t > (topData, idDelDrawEnts); + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_COMMON_SCENE); + auto const tgCS = out.create_targets(rBuilder); + /* unused */ top_emplace< ActiveEntVec_t > (topData, idActiveEntDel); + /* unused */ top_emplace< DrawEntVec_t > (topData, idDrawEntDel); auto &rBasic = top_emplace< ACtxBasic > (topData, idBasic); - auto &rActiveIds = top_emplace< ActiveReg_t > (topData, idActiveIds); auto &rDrawing = top_emplace< ACtxDrawing > (topData, idDrawing); auto &rDrawingRes = top_emplace< ACtxDrawingRes > (topData, idDrawingRes); auto &rNMesh = top_emplace< NamedMeshes > (topData, idNMesh); -// rBuilder.tag(tgEntNew) .depend_on({tgEntDel}); -// rBuilder.tag(tgEntReq) .depend_on({tgEntDel, tgEntNew}); -// rBuilder.tag(tgDelEntReq) .depend_on({tgDelEntMod}); -// rBuilder.tag(tgDelEntClr) .depend_on({tgDelEntMod, tgDelEntReq}); -// rBuilder.tag(tgDelTotalReq) .depend_on({tgDelTotalMod}); -// rBuilder.tag(tgDelTotalClr) .depend_on({tgDelTotalMod, tgDelTotalReq}); -// rBuilder.tag(tgTransformDel) .depend_on({tgTransformMod}); -// rBuilder.tag(tgTransformNew) .depend_on({tgTransformMod, tgTransformDel}); -// rBuilder.tag(tgTransformReq) .depend_on({tgTransformMod, tgTransformDel, tgTransformNew}); -// rBuilder.tag(tgHierNew) .depend_on({tgHierDel}); -// rBuilder.tag(tgHierModEnd) .depend_on({tgHierDel, tgHierNew, tgHierMod}); -// rBuilder.tag(tgHierReq) .depend_on({tgHierMod, tgHierModEnd}); -// rBuilder.tag(tgDrawMod) .depend_on({tgDrawDel}); -// rBuilder.tag(tgDrawReq) .depend_on({tgDrawDel, tgDrawMod}); -// rBuilder.tag(tgDelDrawEntReq) .depend_on({tgDelDrawEntMod}); -// rBuilder.tag(tgDelDrawEntClr) .depend_on({tgDelDrawEntMod, tgDelDrawEntReq}); -// rBuilder.tag(tgMeshMod) .depend_on({tgMeshDel}); -// rBuilder.tag(tgMeshReq) .depend_on({tgMeshDel, tgMeshMod}); -// rBuilder.tag(tgMeshClr) .depend_on({tgMeshDel, tgMeshMod, tgMeshReq}); -// rBuilder.tag(tgTexMod) .depend_on({tgTexDel}); -// rBuilder.tag(tgTexReq) .depend_on({tgTexDel, tgTexMod}); -// rBuilder.tag(tgTexClr) .depend_on({tgTexDel, tgTexMod, tgTexReq}); - - std::vector tasks; - rBuilder.task() - .push_to(tasks) - .name("Set entity meshes and textures dirty") - .depends_on({}) - .data({ idDrawing}) + .name ("Set materials, meshes, and textures dirty") + .trigger_on ({tgScn.resyncAll}) + .fulfills ({tgCS.texture_mod, tgCS.mesh_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing }) .func([] (ACtxDrawing& rDrawing) noexcept { SysRender::set_dirty_all(rDrawing); }); - - - scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelEntReq, tgDelTotalMod}).data( - "Create DeleteTotal vector, which includes descendents of deleted hierarchy entities", - TopDataIds_t{ idBasic, idDelEnts, idDelTotal}, - wrap_args([] (ACtxBasic& rBasic, EntVector_t const& rDelEnts, EntVector_t& rDelTotal) noexcept + rBuilder.task() + .name ("Delete ActiveEnt IDs") + .trigger_on ({tgCS.delActiveEnt_mod}) + .fulfills ({tgCS.delActiveEnt_use, tgCS.activeEnt_del, tgCS.activeEnt_mod}) + .push_to (out.m_tasks) + .args ({ idBasic, idActiveEntDel }) + .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept { - auto const &delFirst = std::cbegin(rDelEnts); - auto const &delLast = std::cend(rDelEnts); - - rDelTotal.assign(delFirst, delLast); - - for (ActiveEnt root : rDelEnts) + for (ActiveEnt const ent : rActiveEntDel) { - for (ActiveEnt descendant : SysSceneGraph::descendants(rBasic.m_scnGraph, root)) + if (rBasic.m_activeIds.exists(ent)) { - rDelTotal.push_back(descendant); + rBasic.m_activeIds.remove(ent); } } - })); + }); - scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelEntReq, tgHierMod}).data( - "Cut deleted entities out of hierarchy", - TopDataIds_t{ idBasic, idDelEnts}, - wrap_args([] (ACtxBasic& rBasic, EntVector_t const& rDelEnts) noexcept + rBuilder.task() + .name ("Delete basic components") + .trigger_on ({tgCS.delActiveEnt_mod}) + .fulfills ({tgCS.delActiveEnt_use, tgCS.transform_del, tgCS.transform_mod}) + .push_to (out.m_tasks) + .args ({ idBasic, idActiveEntDel }) + .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept { - auto const &delFirst = std::cbegin(rDelEnts); - auto const &delLast = std::cend(rDelEnts); - - SysSceneGraph::cut(rBasic.m_scnGraph, delFirst, delLast); - })); + update_delete_basic(rBasic, rActiveEntDel.cbegin(), rActiveEntDel.cend()); + }); - scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgEntDel}).data( - "Delete Entity IDs", - TopDataIds_t{ idActiveIds, idDelTotal}, - wrap_args([] (ActiveReg_t& rActiveIds, EntVector_t const& rDelTotal) noexcept + rBuilder.task() + .name ("Delete DrawEntity of deleted ActiveEnts") + .trigger_on ({tgCS.delActiveEnt_mod}) + .fulfills ({tgCS.delActiveEnt_use, tgCS.delDrawEnt_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing, idActiveEntDel, idDrawEntDel }) + .func([] (ACtxDrawing& rDrawing, ActiveEntVec_t const& rActiveEntDel, DrawEntVec_t& rDrawEntDel) noexcept { - for (ActiveEnt const ent : rDelTotal) + for (ActiveEnt const ent : rActiveEntDel) { - if (rActiveIds.exists(ent)) + DrawEnt const drawEnt = std::exchange(rDrawing.m_activeToDraw[ent], lgrn::id_null()); + if (drawEnt != lgrn::id_null()) { - rActiveIds.remove(ent); + rDrawEntDel.push_back(drawEnt); } } - })); + }); - scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgTransformDel, tgHierDel}).data( - "Delete basic components", - TopDataIds_t{ idBasic, idDelTotal}, - wrap_args([] (ACtxBasic& rBasic, EntVector_t const& rDelTotal) noexcept + rBuilder.task() + .name ("Delete drawing components") + .trigger_on ({tgCS.delDrawEnt_mod}) + .fulfills ({tgCS.delDrawEnt_use, tgCS.mesh_del, tgCS.mesh_mod, tgCS.texture_del, tgCS.texture_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawEntDel }) + .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept { - update_delete_basic(rBasic, std::cbegin(rDelTotal), std::cend(rDelTotal)); - })); + SysRender::update_delete_drawing(rDrawing, rDrawEntDel.cbegin(), rDrawEntDel.cend()); + }); - scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgDrawDel}).data( - "Delete DrawEntity of deleted ActiveEnts", - TopDataIds_t{ idDrawing, idDelTotal, idDelDrawEnts}, - wrap_args([] (ACtxDrawing& rDrawing, EntVector_t const& rDelTotal, DrawEntVector_t& rDelDrawEnts) noexcept + rBuilder.task() + .name ("Delete DrawEntity IDs") + .trigger_on ({tgCS.delDrawEnt_mod}) + .fulfills ({tgCS.delDrawEnt_use, tgCS.drawEnt_del, tgCS.drawEnt_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawEntDel }) + .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept { - for (ActiveEnt const ent : rDelTotal) + for (DrawEnt const drawEnt : rDrawEntDel) { - DrawEnt const drawEnt = std::exchange(rDrawing.m_activeToDraw[ent], lgrn::id_null()); - if (drawEnt != lgrn::id_null()) + if (rDrawing.m_drawIds.exists(drawEnt)) { - rDelDrawEnts.push_back(drawEnt); + rDrawing.m_drawIds.remove(drawEnt); } } - })); - - scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelDrawEntReq, tgDrawDel}).data( - "Delete drawing components", - TopDataIds_t{ idDrawing, idDelDrawEnts}, - wrap_args([] (ACtxDrawing& rDrawing, DrawEntVector_t const& rDelDrawEnts) noexcept - { - SysRender::update_delete_drawing(rDrawing, rDelDrawEnts.cbegin(), rDelDrawEnts.cend()); - })); + }); - scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelDrawEntReq}).data( - "Delete DrawEntity IDs", - TopDataIds_t{ idDrawing, idDelDrawEnts}, - wrap_args([] (ACtxDrawing& rDrawing, DrawEntVector_t const& rDelDrawEnts) noexcept + rBuilder.task() + .name ("Delete DrawEnt from materials") + .trigger_on ({tgCS.delDrawEnt_mod}) + .fulfills ({tgCS.delDrawEnt_use, tgCS.material_del, tgCS.material_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawEntDel }) + .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept { - for (DrawEnt const drawEnt : rDelDrawEnts) + for (DrawEnt const ent : rDrawEntDel) { - if (rDrawing.m_drawIds.exists(drawEnt)) + for (Material &rMat : rDrawing.m_materials) { - rDrawing.m_drawIds.remove(drawEnt); + rMat.m_ents.reset(std::size_t(ent)); } } - })); + }); + + rBuilder.task() + .name ("Clear delete vectors once we're done with it") + .trigger_on ({tgCS.delActiveEnt_use, tgCS.delDrawEnt_use}) + .fulfills ({tgCS.delActiveEnt_clr, tgCS.delDrawEnt_clr}) + .push_to (out.m_tasks) + .args ({ idActiveEntDel, idDrawEntDel }) + .func([] (ActiveEntVec_t& idActiveEntDel, DrawEntVec_t& rDrawEntDel) noexcept + { + idActiveEntDel.clear(); + rDrawEntDel.clear(); + }); - scnCommon.task() = rBuilder.task().assign({tgSceneEvt, tgDelEntClr, tgDelDrawEntClr}).data( - "Clear delete vectors once we're done with it", - TopDataIds_t{ idDelEnts, idDelDrawEnts}, - wrap_args([] (EntVector_t& rDelEnts, DrawEntVector_t& rDelDrawEnts) noexcept + rBuilder.task() + .name ("Clear material dirty vectors once we're done with it") + .trigger_on ({tgCS.material_use}) + .fulfills ({tgCS.material_clr}) + .push_to (out.m_tasks) + .args ({ idDrawing }) + .func([] (ACtxDrawing& rDrawing) noexcept { - rDelEnts.clear(); - rDelDrawEnts.clear(); - })); + for (std::size_t const materialInt : rDrawing.m_materialIds.bitview().zeros()) + { + rDrawing.m_materials[MaterialId(materialInt)].m_dirty.clear(); + } + }); - scnCommon.task() = rBuilder.task().assign({tgCleanupEvt}).data( - "Clean up scene and resource owners", - TopDataIds_t{ idDrawing, idDrawingRes, idResources}, - wrap_args([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources) noexcept + rBuilder.task() + .name ("Clean up scene and resource owners") + .trigger_on ({tgScn.cleanup}) + .depends_on ({tgCS.mesh_use, tgCS.texture_use}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawingRes, idResources}) + .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources) noexcept { SysRender::clear_owners(rDrawing); SysRender::clear_resource_owners(rDrawingRes, rResources); - })); - - - // Convenient functor to get a reference-counted mesh owner - auto const quick_add_mesh = SysRender::gen_drawable_mesh_adder(rDrawing, rDrawingRes, rResources, pkg); + }); - scnCommon.task() = rBuilder.task().assign({tgCleanupEvt}).data( - "Clean up NamedMeshes mesh and texture owners", - TopDataIds_t{ idDrawing, idNMesh}, - wrap_args([] (ACtxDrawing& rDrawing, NamedMeshes& rNMesh) noexcept + rBuilder.task() + .name ("Clean up NamedMeshes mesh and texture owners") + .trigger_on ({tgScn.cleanup}) + .fulfills ({}) + .push_to (out.m_tasks) + .args ({ idDrawing, idNMesh }) + .func([] (ACtxDrawing& rDrawing, NamedMeshes& rNMesh) noexcept { for ([[maybe_unused]] auto && [_, rOwner] : std::exchange(rNMesh.m_shapeToMesh, {})) { @@ -229,7 +233,10 @@ Session setup_common_scene( { rDrawing.m_meshRefCounts.ref_release(std::move(rOwner)); } - })); + }); + + // Convenient functor to get a reference-counted mesh owner + auto const quick_add_mesh = SysRender::gen_drawable_mesh_adder(rDrawing, rDrawingRes, rResources, pkg); // Acquire mesh resources from Package using osp::phys::EShape; @@ -238,72 +245,7 @@ Session setup_common_scene( rNMesh.m_shapeToMesh.emplace(EShape::Sphere, quick_add_mesh("sphere")); rNMesh.m_namedMeshs.emplace("floor", quick_add_mesh("grid64solid")); - - return scnCommon; + return out; } - -Session setup_material( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Session const& scnCommon) -{ - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - - Session material; - OSP_SESSION_ACQUIRE_DATA(material, topData, TESTAPP_MATERIAL); - OSP_SESSION_ACQUIRE_TAGS(material, rTags, TESTAPP_MATERIAL); - - top_emplace< EntSet_t > (topData, idMatEnts); - top_emplace< std::vector > (topData, idMatDirty); - - rBuilder.tag(tgMatMod) .depend_on({tgMatDel}); - rBuilder.tag(tgMatReq) .depend_on({tgMatDel, tgMatMod}); - rBuilder.tag(tgMatClr) .depend_on({tgMatDel, tgMatMod, tgMatReq}); - - material.task() = rBuilder.task().assign({tgResyncEvt}).data( - "Set all X material entities as dirty", - TopDataIds_t{ idMatEnts, idMatDirty}, - wrap_args([] (EntSet_t const& rMatEnts, std::vector& rMatDirty) noexcept - { - for (std::size_t const entInt : rMatEnts.ones()) - { - rMatDirty.push_back(DrawEnt(entInt)); - } - })); - - material.task() = rBuilder.task().assign({tgSceneEvt, tgSyncEvt, tgMatClr}).data( - "Clear dirty vectors for material", - TopDataIds_t{ idMatDirty}, - wrap_args([] (std::vector& rMatDirty) noexcept - { - rMatDirty.clear(); - })); - - material.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgMatDel}).data( - "Delete material components", - TopDataIds_t{ idDrawing, idDelTotal, idMatEnts}, - wrap_args([] (ACtxDrawing const& rDrawing, EntVector_t const& rDelTotal, EntSet_t& rMatEnts) noexcept - { - for (ActiveEnt const ent : rDelTotal) - { - DrawEnt const drawEnt = rDrawing.m_activeToDraw[ent]; - - if (drawEnt != lgrn::id_null()) - { - rMatEnts.reset(std::size_t(drawEnt)); - } - } - })); - - return material; -} - -#endif - } // namespace testapp::scenes - - - - diff --git a/src/test_application/activescenes/scene_common.h b/src/test_application/activescenes/scene_common.h index 9bddb0cf..f0e2253f 100644 --- a/src/test_application/activescenes/scene_common.h +++ b/src/test_application/activescenes/scene_common.h @@ -35,14 +35,6 @@ namespace testapp::scenes { -inline auto const delete_ent_set = osp::wrap_args([] (osp::active::EntSet_t& rSet, osp::active::EntVector_t const& rDelTotal) noexcept -{ - for (osp::active::ActiveEnt const ent : rDelTotal) - { - rSet.reset(std::size_t(ent)); - } -}); - struct NamedMeshes { // Required for std::is_copy_assignable to work properly inside of entt::any @@ -56,23 +48,19 @@ struct NamedMeshes osp::active::MeshIdOwner_t> m_namedMeshs; }; +osp::Session setup_scene( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView const topData); + /** * @brief Support for Time, ActiveEnts, Hierarchy, Transforms, Drawing, and more... */ osp::Session setup_common_scene( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& scene, osp::TopDataId idResources, osp::PkgId pkg); -/** - * @brief Support a single material, aka: a Set of ActiveEnts and dirty flags - * - * Multiple material sessions can be setup for each material - */ -osp::Session setup_material( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& scnCommon); } diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index c55d595d..96d37aed 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -51,28 +51,41 @@ using namespace Magnum::Math::Literals; namespace testapp::scenes { -#if 0 +void create_materials( + ArrayView const topData, + Session const& commonScene, + int const count) +{ + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + auto &rDrawing = top_get< ACtxDrawing >(topData, idDrawing); + + for (int i = 0; i < count; ++i) + { + [[maybe_unused]] MaterialId const mat = rDrawing.m_materialIds.create(); + LGRN_ASSERT(int(mat) == i); + } + + rDrawing.m_materials.resize(count); +} void add_floor( ArrayView const topData, - Session const& scnCommon, - Session const& material, + Session const& commonScene, Session const& shapeSpawn, + MaterialId const materialId, TopDataId const idResources, PkgId const pkg) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); - OSP_SESSION_UNPACK_DATA(shapeSpawn, TESTAPP_SHAPE_SPAWN); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); - auto &rResources = top_get< Resources > (topData, idResources); - auto &rActiveIds = top_get< ActiveReg_t > (topData, idActiveIds); - auto &rBasic = top_get< ACtxBasic > (topData, idBasic); - auto &rDrawing = top_get< ACtxDrawing > (topData, idDrawing); - auto &rDrawingRes = top_get< ACtxDrawingRes > (topData, idDrawingRes); - auto &rMatEnts = top_get< EntSet_t > (topData, idMatEnts); - auto &rMatDirty = top_get< std::vector > (topData, idMatDirty); - auto &rSpawner = top_get< SpawnerVec_t > (topData, idSpawner); + auto &rResources = top_get< Resources > (topData, idResources); + auto &rBasic = top_get< ACtxBasic > (topData, idBasic); + auto &rDrawing = top_get< ACtxDrawing > (topData, idDrawing); + auto &rDrawingRes = top_get< ACtxDrawingRes > (topData, idDrawingRes); + auto &rSpawner = top_get< ACtxShapeSpawner > (topData, idSpawner); + + Material &rMaterial = rDrawing.m_materials.at(materialId); // Convenient functor to get a reference-counted mesh owner auto const quick_add_mesh = SysRender::gen_drawable_mesh_adder(rDrawing, rDrawingRes, rResources, pkg); @@ -83,15 +96,15 @@ void add_floor( static constexpr Vector3 const sc_floorPos{0.0f, 0.0f, -1.005f}; // Create floor root and mesh entity - ActiveEnt const floorRootEnt = rActiveIds.create(); - ActiveEnt const floorMeshEnt = rActiveIds.create(); + ActiveEnt const floorRootEnt = rBasic.m_activeIds.create(); + ActiveEnt const floorMeshEnt = rBasic.m_activeIds.create(); DrawEnt const floorMeshDrawEnt = rDrawing.m_drawIds.create(); // Resize some containers to fit all existing entities - rBasic.m_scnGraph.resize(rActiveIds.capacity()); - rDrawing.resize_active(rActiveIds.capacity()); + rBasic.m_scnGraph.resize(rBasic.m_activeIds.capacity()); + rDrawing.resize_active(rBasic.m_activeIds.capacity()); rDrawing.resize_draw(); - bitvector_resize(rMatEnts, rDrawing.m_drawIds.capacity()); + bitvector_resize(rMaterial.m_ents, rDrawing.m_drawIds.capacity()); rBasic.m_transform.emplace(floorRootEnt); @@ -101,9 +114,8 @@ void add_floor( rDrawing.m_meshDirty.push_back(floorMeshDrawEnt); // Add mesh visualizer material to floor mesh entity - - rMatEnts.set(std::size_t(floorMeshDrawEnt)); - rMatDirty.push_back(floorMeshDrawEnt); + rMaterial.m_ents.set(std::size_t(floorMeshDrawEnt)); + rMaterial.m_dirty.push_back(floorMeshDrawEnt); // Add transform, draw transform, opaque, and visible entity rBasic.m_transform.emplace(floorMeshEnt, ACompTransform{Matrix4::scaling(sc_floorSize)}); @@ -121,7 +133,7 @@ void add_floor( bldFloorRoot.add_child(floorMeshEnt); // Add collider to floor root entity - rSpawner.emplace_back(SpawnShape{ + rSpawner.m_spawnRequest.emplace_back(SpawnShape{ .m_position = sc_floorPos, .m_velocity = sc_floorSize, .m_size = sc_floorSize, @@ -130,88 +142,99 @@ void add_floor( }); } + + Session setup_camera_ctrl( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& app, + Session const& windowApp, Session const& scnRender) { - OSP_SESSION_UNPACK_DATA(app, TESTAPP_APP); - OSP_SESSION_UNPACK_TAGS(app, TESTAPP_APP); - OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); - auto &rUserInput = top_get< osp::input::UserInputHandler >(topData, idUserInput); + OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); + OSP_DECLARE_GET_DATA_IDS(scnRender, TESTAPP_DATA_COMMON_RENDERER); + + auto const tgSR = scnRender.get_targets(); - Session cameraCtrl; - OSP_SESSION_ACQUIRE_DATA(cameraCtrl, topData, TESTAPP_CAMERA_CTRL); - OSP_SESSION_ACQUIRE_TAGS(cameraCtrl, rTags, TESTAPP_CAMERA_CTRL); + auto &rUserInput = top_get< osp::input::UserInputHandler >(topData, idUserInput); - rBuilder.tag(tgCamCtrlReq).depend_on({tgCamCtrlMod}); + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_CAMERA_CTRL); + auto const tgCmCt = out.create_targets(rBuilder); top_emplace< ACtxCameraController > (topData, idCamCtrl, rUserInput); - cameraCtrl.task() = rBuilder.task().assign({tgRenderEvt, tgCamCtrlReq, tgCameraMod}).data( - "Position Rendering Camera according to Camera Controller", - TopDataIds_t{ idCamCtrl, idCamera}, - wrap_args([] (ACtxCameraController const& rCamCtrl, Camera &rCamera) noexcept + rBuilder.task() + .name ("Position Rendering Camera according to Camera Controller") + .trigger_on ({tgCmCt.cameraCtrl_mod}) + .fulfills ({tgCmCt.cameraCtrl_use, tgSR.camera_mod}) + .push_to (out.m_tasks) + .args ({ idCamCtrl, idCamera }) + .func([] (ACtxCameraController const& rCamCtrl, Camera &rCamera) noexcept { rCamera.m_transform = rCamCtrl.m_transform; - })); + }); - return cameraCtrl; + return out; } Session setup_camera_free( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& app, - Session const& scnCommon, - Session const& camera) + Session const& windowApp, + Session const& scene, + Session const& cameraCtrl) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(app, TESTAPP_APP); - OSP_SESSION_UNPACK_DATA(camera, TESTAPP_CAMERA_CTRL); - OSP_SESSION_UNPACK_TAGS(camera, TESTAPP_CAMERA_CTRL); + OSP_DECLARE_GET_DATA_IDS(scene, TESTAPP_DATA_SCENE); + OSP_DECLARE_GET_DATA_IDS(cameraCtrl, TESTAPP_DATA_CAMERA_CTRL); - Session cameraFree; + auto const tgWin = windowApp .get_targets(); + auto const tgCmCt = cameraCtrl .get_targets(); - cameraFree.task() = rBuilder.task().assign({tgInputEvt, tgCamCtrlMod}).data( - "Move Camera", - TopDataIds_t{ idCamCtrl, idDeltaTimeIn}, - wrap_args([] (ACtxCameraController& rCamCtrl, float const deltaTimeIn) noexcept + Session out; + + rBuilder.task() + .name ("Move Camera controller") + .trigger_on ({tgWin.input}) + .fulfills ({tgCmCt.cameraCtrl_mod}) + .push_to (out.m_tasks) + .args ({ idCamCtrl, idDeltaTimeIn }) + .func([] (ACtxCameraController& rCamCtrl, float const deltaTimeIn) noexcept { SysCameraController::update_view(rCamCtrl, deltaTimeIn); SysCameraController::update_move(rCamCtrl, deltaTimeIn, true); - })); + }); - return cameraFree; + return out; } Session setup_thrower( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& magnum, - Session const& renderer, - Session const& simpleCamera, + Session const& windowApp, + Session const& cameraCtrl, Session const& shapeSpawn) { - OSP_SESSION_UNPACK_DATA(shapeSpawn, TESTAPP_SHAPE_SPAWN); - OSP_SESSION_UNPACK_TAGS(shapeSpawn, TESTAPP_SHAPE_SPAWN); - OSP_SESSION_UNPACK_TAGS(magnum, TESTAPP_APP_MAGNUM); - OSP_SESSION_UNPACK_DATA(simpleCamera, TESTAPP_CAMERA_CTRL); - OSP_SESSION_UNPACK_TAGS(simpleCamera, TESTAPP_CAMERA_CTRL); - + OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); + OSP_DECLARE_GET_DATA_IDS(cameraCtrl, TESTAPP_DATA_CAMERA_CTRL); auto &rCamCtrl = top_get< ACtxCameraController > (topData, idCamCtrl); - Session thrower; - auto const [idBtnThrow] = thrower.acquire_data<1>(topData); + auto const tgWin = windowApp .get_targets(); + auto const tgCmCt = cameraCtrl .get_targets(); + auto const tgShSp = shapeSpawn .get_targets(); + + Session out; + auto const [idBtnThrow] = out.acquire_data<1>(topData); top_emplace< EButtonControlIndex > (topData, idBtnThrow, rCamCtrl.m_controls.button_subscribe("debug_throw")); - thrower.task() = rBuilder.task().assign({tgInputEvt, tgSpawnMod, tgCamCtrlReq}).data( - "Throw spheres when pressing space", - TopDataIds_t{ idCamCtrl, idSpawner, idBtnThrow}, - wrap_args([] (ACtxCameraController& rCamCtrl, SpawnerVec_t& rSpawner, EButtonControlIndex btnThrow) noexcept + rBuilder.task() + .name ("Throw spheres when pressing space") + .trigger_on ({tgWin.input}) + .depends_on ({tgCmCt.cameraCtrl_mod}) + .fulfills ({tgShSp.spawnRequest_mod}) + .push_to (out.m_tasks) + .args ({ idCamCtrl, idSpawner, idBtnThrow }) + .func([] (ACtxCameraController& rCamCtrl, ACtxShapeSpawner& rSpawner, EButtonControlIndex btnThrow) noexcept { // Throw a sphere when the throw button is pressed if (rCamCtrl.m_controls.button_held(btnThrow)) @@ -219,29 +242,29 @@ Session setup_thrower( Matrix4 const &camTf = rCamCtrl.m_transform; float const speed = 120; float const dist = 8.0f; - rSpawner.emplace_back(SpawnShape{ + rSpawner.m_spawnRequest.emplace_back(SpawnShape{ .m_position = camTf.translation() - camTf.backward() * dist, .m_velocity = -camTf.backward() * speed, .m_size = Vector3{1.0f}, .m_mass = 1.0f, .m_shape = EShape::Sphere }); - return osp::TopTaskStatus::Success; } - return osp::TopTaskStatus::Success; - })); + }); - return thrower; + return out; } +/* + Session setup_droppers( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& scnCommon, + Session const& commonScene, Session const& shapeSpawn) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(shapeSpawn, TESTAPP_SHAPE_SPAWN); OSP_SESSION_UNPACK_TAGS(shapeSpawn, TESTAPP_SHAPE_SPAWN); @@ -292,8 +315,6 @@ Session setup_droppers( })); return droppers; -} - -#endif +}*/ } // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_misc.h b/src/test_application/activescenes/scene_misc.h index 054cf1c8..07f7ca10 100644 --- a/src/test_application/activescenes/scene_misc.h +++ b/src/test_application/activescenes/scene_misc.h @@ -26,14 +26,21 @@ #include "scenarios.h" +#include + namespace testapp::scenes { +void create_materials( + osp::ArrayView topData, + osp::Session const& commonScene, + int count); + void add_floor( osp::ArrayView topData, - osp::Session const& scnCommon, - osp::Session const& material, + osp::Session const& commonScene, osp::Session const& shapeSpawn, + osp::active::MaterialId material, osp::TopDataId idResources, osp::PkgId pkg); @@ -43,7 +50,7 @@ void add_floor( osp::Session setup_camera_ctrl( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& app, + osp::Session const& windowApp, osp::Session const& scnRender); /** @@ -52,8 +59,8 @@ osp::Session setup_camera_ctrl( osp::Session setup_camera_free( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& app, - osp::Session const& scnCommon, + osp::Session const& windowApp, + osp::Session const& scene, osp::Session const& camera); /** @@ -62,9 +69,8 @@ osp::Session setup_camera_free( osp::Session setup_thrower( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& magnum, - osp::Session const& renderer, - osp::Session const& simpleCamera, + osp::Session const& windowApp, + osp::Session const& cameraCtrl, osp::Session const& shapeSpawn); /** @@ -73,7 +79,7 @@ osp::Session setup_thrower( osp::Session setup_droppers( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& shapeSpawn); } diff --git a/src/test_application/activescenes/scene_newton.cpp b/src/test_application/activescenes/scene_newton.cpp index 30d5027f..b81c0138 100644 --- a/src/test_application/activescenes/scene_newton.cpp +++ b/src/test_application/activescenes/scene_newton.cpp @@ -56,147 +56,152 @@ using Corrade::Containers::arrayView; namespace testapp::scenes { -#if 0 - Session setup_newton( TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, - Session const& scnCommon, + osp::Session const& scene, + Session const& commonScene, Session const& physics) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); + OSP_DECLARE_GET_DATA_IDS(scene, TESTAPP_DATA_SCENE); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); - Session newton; - OSP_SESSION_ACQUIRE_DATA(newton, topData, TESTAPP_NEWTON); - OSP_SESSION_ACQUIRE_TAGS(newton, rTags, TESTAPP_NEWTON); + auto const tgScn = scene .get_targets(); + auto const tgCS = commonScene .get_targets(); + auto const tgPhy = physics .get_targets(); - rBuilder.tag(tgNwtBodyDel).depend_on({tgNwtBodyPrv}); - rBuilder.tag(tgNwtBodyMod).depend_on({tgNwtBodyPrv, tgNwtBodyDel}); - rBuilder.tag(tgNwtBodyReq).depend_on({tgNwtBodyPrv, tgNwtBodyDel, tgNwtBodyMod}); - rBuilder.tag(tgNwtBodyClr).depend_on({tgNwtBodyPrv, tgNwtBodyDel, tgNwtBodyMod, tgNwtBodyReq}); + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_NEWTON); + auto const tgNwt = out.create_targets(rBuilder); top_emplace< ACtxNwtWorld >(topData, idNwt, 2); using ospnewton::SysNewton; - newton.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgNwtBodyDel}).data( - "Delete Newton components", - TopDataIds_t{ idNwt, idDelTotal}, - wrap_args([] (ACtxNwtWorld& rNwt, EntVector_t const& rDelTotal) noexcept + rBuilder.task() + .name ("Delete Newton components") + .trigger_on ({tgCS.delActiveEnt_mod}) + .fulfills ({tgCS.delActiveEnt_use, tgNwt.nwtBody_del, tgNwt.nwtBody_new}) + .push_to (out.m_tasks) + .args({ idNwt, idActiveEntDel }) + .func([] (ACtxNwtWorld& rNwt, ActiveEntVec_t const& rActiveEntDel) noexcept { - SysNewton::update_delete (rNwt, std::cbegin(rDelTotal), std::cend(rDelTotal)); - })); - - newton.task() = rBuilder.task().assign({tgTimeEvt, tgPhysPrv, tgNwtBodyPrv, tgPhysTransformMod, tgTransformMod}).data( - "Update Newton world", - TopDataIds_t{ idBasic, idPhys, idNwt, idDeltaTimeIn }, - wrap_args([] (ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, float const deltaTimeIn) noexcept + SysNewton::update_delete (rNwt, rActiveEntDel.cbegin(), rActiveEntDel.cend()); + }); + + rBuilder.task() + .name ("Update Newton world") + .trigger_on ({tgScn.time}) + .depends_on ({tgNwt.nwtBody_mod, tgCS.hier_mod, tgPhy.physics_mod, tgCS.transform_new}) + .fulfills ({tgNwt.nwtBody_use, tgCS.hier_use, tgPhy.physics_use, tgCS.transform_mod}) + .push_to (out.m_tasks) + .args({ idBasic, idPhys, idNwt, idDeltaTimeIn }) + .func([] (ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, float const deltaTimeIn, WorkerContext ctx) noexcept { SysNewton::update_world(rPhys, rNwt, deltaTimeIn, rBasic.m_scnGraph, rBasic.m_transform); - })); + }); top_emplace< ACtxNwtWorld >(topData, idNwt, 2); - return newton; + return out; } + + osp::Session setup_newton_factors( TopTaskBuilder& rBuilder, - ArrayView topData, - Tags& rTags) + ArrayView topData) { - Session nwtFactors; - OSP_SESSION_ACQUIRE_DATA(nwtFactors, topData, TESTAPP_NEWTON_FORCES); + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_NEWTON_FORCES); auto &rFactors = top_emplace(topData, idNwtFactors); std::fill(rFactors.begin(), rFactors.end(), 0); - return nwtFactors; + return out; } -osp::Session setup_newton_force_accel( - TopTaskBuilder& rBuilder, - ArrayView topData, - Tags& rTags, - Session const& newton, - Session const& nwtFactors, - Vector3 accel) -{ - using UserData_t = ACtxNwtWorld::ForceFactorFunc::UserData_t; - OSP_SESSION_UNPACK_DATA(newton, TESTAPP_NEWTON); - OSP_SESSION_UNPACK_DATA(nwtFactors, TESTAPP_NEWTON_FORCES); +//osp::Session setup_newton_force_accel( +// TopTaskBuilder& rBuilder, +// ArrayView topData, +// Session const& newton, +// Session const& nwtFactors, +// Vector3 accel) +//{ +// using UserData_t = ACtxNwtWorld::ForceFactorFunc::UserData_t; +// OSP_SESSION_UNPACK_DATA(newton, TESTAPP_NEWTON); +// OSP_SESSION_UNPACK_DATA(nwtFactors, TESTAPP_NEWTON_FORCES); - auto &rNwt = top_get(topData, idNwt); +// auto &rNwt = top_get(topData, idNwt); - Session nwtAccel; - OSP_SESSION_ACQUIRE_DATA(nwtAccel, topData, TESTAPP_NEWTON_ACCEL); +// Session nwtAccel; +// OSP_SESSION_ACQUIRE_DATA(nwtAccel, topData, TESTAPP_NEWTON_ACCEL); - auto &rAccel = top_emplace(topData, idAcceleration, accel); +// auto &rAccel = top_emplace(topData, idAcceleration, accel); - ACtxNwtWorld::ForceFactorFunc const factor - { - .m_func = [] (NewtonBody const* pBody, BodyId const bodyID, ACtxNwtWorld const& rNwt, UserData_t data, Vector3& rForce, Vector3& rTorque) noexcept - { - float mass = 0.0f; - float dummy = 0.0f; - NewtonBodyGetMass(pBody, &mass, &dummy, &dummy, &dummy); - - auto const& force = *reinterpret_cast(data[0]); - rForce += force * mass; - }, - .m_userData = {&rAccel} - }; +// ACtxNwtWorld::ForceFactorFunc const factor +// { +// .m_func = [] (NewtonBody const* pBody, BodyId const bodyID, ACtxNwtWorld const& rNwt, UserData_t data, Vector3& rForce, Vector3& rTorque) noexcept +// { +// float mass = 0.0f; +// float dummy = 0.0f; +// NewtonBodyGetMass(pBody, &mass, &dummy, &dummy, &dummy); - // Register force +// auto const& force = *reinterpret_cast(data[0]); +// rForce += force * mass; +// }, +// .m_userData = {&rAccel} +// }; - std::size_t const index = rNwt.m_factors.size(); - rNwt.m_factors.emplace_back(factor); +// // Register force - auto factorBits = lgrn::bit_view(top_get(topData, idNwtFactors)); - factorBits.set(index); +// std::size_t const index = rNwt.m_factors.size(); +// rNwt.m_factors.emplace_back(factor); - return nwtAccel; -} +// auto factorBits = lgrn::bit_view(top_get(topData, idNwtFactors)); +// factorBits.set(index); + +// return nwtAccel; +//} Session setup_shape_spawn_newton( TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, - Session const& scnCommon, + Session const& commonScene, Session const& physics, Session const& shapeSpawn, Session const& newton, Session const& nwtFactors) { - Session shapeSpawnNwt; - - - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_DATA(shapeSpawn, TESTAPP_SHAPE_SPAWN); - OSP_SESSION_UNPACK_TAGS(shapeSpawn, TESTAPP_SHAPE_SPAWN); - OSP_SESSION_UNPACK_DATA(newton, TESTAPP_NEWTON); - OSP_SESSION_UNPACK_TAGS(newton, TESTAPP_NEWTON); - OSP_SESSION_UNPACK_DATA(nwtFactors, TESTAPP_NEWTON_FORCES); - - shapeSpawnNwt.task() = rBuilder.task().assign({tgSceneEvt, tgSpawnReq, tgSpawnEntReq, tgNwtBodyMod}).data( - "Add physics to spawned shapes", - TopDataIds_t{ idActiveIds, idSpawner, idSpawnerEnts, idPhys, idNwt, idNwtFactors }, - wrap_args([] (ActiveReg_t const &rActiveIds, SpawnerVec_t& rSpawner, EntVector_t& rSpawnerEnts, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ForceFactors_t nwtFactors) noexcept + + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); + OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); + OSP_DECLARE_GET_DATA_IDS(newton, TESTAPP_DATA_NEWTON); + OSP_DECLARE_GET_DATA_IDS(nwtFactors, TESTAPP_DATA_NEWTON_FORCES); + + auto const tgShSp = shapeSpawn .get_targets(); + auto const tgNwt = newton .get_targets(); + + Session out; + + rBuilder.task() + .name ("Add Newton physics to spawned shapes") + .trigger_on ({tgShSp.spawnRequest_mod}) + .depends_on ({tgShSp.spawnedEnts_mod}) + .fulfills ({tgShSp.spawnedEnts_use, tgNwt.nwtBody_new, tgNwt.nwtBody_mod}) + .push_to (out.m_tasks) + .args({ idBasic, idSpawner, idPhys, idNwt, idNwtFactors }) + .func([] (ACtxBasic const &rBasic, ACtxShapeSpawner& rSpawner, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ForceFactors_t nwtFactors) noexcept { - for (std::size_t i = 0; i < rSpawner.size(); ++i) + for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) { - SpawnShape const &spawn = rSpawner[i]; - ActiveEnt const root = rSpawnerEnts[i * 2]; - ActiveEnt const child = rSpawnerEnts[i * 2 + 1]; + SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; + ActiveEnt const root = rSpawner.m_ents[i * 2]; + ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; NwtColliderPtr_t pCollision{ SysNewton::create_primative(rNwt, spawn.m_shape) }; SysNewton::orient_collision(pCollision.get(), spawn.m_shape, {0.0f, 0.0f, 0.0f}, Matrix3{}, spawn.m_size); @@ -220,9 +225,9 @@ Session setup_shape_spawn_newton( NewtonBodySetTransformCallback(pBody, &SysNewton::cb_set_transform); SysNewton::set_userdata_bodyid(pBody, bodyId); } - })); + }); - return shapeSpawnNwt; + return out; } void compound_collect_recurse( @@ -272,11 +277,12 @@ void compound_collect_recurse( } +#if 0 + Session setup_vehicle_spawn_newton( TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, - Session const& scnCommon, + Session const& commonScene, Session const& physics, Session const& prefabs, Session const& parts, @@ -284,8 +290,8 @@ Session setup_vehicle_spawn_newton( Session const& newton, TopDataId const idResources) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(physics, TESTAPP_PHYSICS); OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); OSP_SESSION_UNPACK_DATA(prefabs, TESTAPP_PREFABS); @@ -304,10 +310,15 @@ Session setup_vehicle_spawn_newton( rBuilder.tag(tgNwtVhWeldEntReq) .depend_on({tgNwtVhWeldEntMod}); rBuilder.tag(tgNwtVhHierReq) .depend_on({tgNwtVhHierMod}); + rBuilder.task() + .name ("aaa") + .depends_on ({}) + .fulfills ({}) + .push_to (out.m_tasks) vehicleSpawnNwt.task() = rBuilder.task().assign({tgSceneEvt, tgEntNew, tgNwtVhWeldEntMod}).data( "Create entity for each rigid group", - TopDataIds_t{ idActiveIds, idVehicleSpawn, idScnParts }, - wrap_args([] (ActiveReg_t& rActiveIds, ACtxVehicleSpawn& rVehicleSpawn, ACtxParts& rScnParts) noexcept + .args({ idActiveIds, idVehicleSpawn, idScnParts }) + .func([] (ActiveReg_t& rActiveIds, ACtxVehicleSpawn& rVehicleSpawn, ACtxParts& rScnParts) noexcept { if (rVehicleSpawn.new_vehicle_count() == 0) { @@ -326,10 +337,15 @@ Session setup_vehicle_spawn_newton( } })); + rBuilder.task() + .name ("aaa") + .depends_on ({}) + .fulfills ({}) + .push_to (out.m_tasks) vehicleSpawnNwt.task() = rBuilder.task().assign({tgSceneEvt, tgVsBasicInReq, tgVsWeldReq, tgNwtVhWeldEntReq, tgPrefabEntReq, tgNwtVhHierMod, tgPfParentHierMod, tgHierMod, tgTransformNew}).data( "Add vehicle entities to Scene Graph", - TopDataIds_t{ idBasic, idActiveIds, idVehicleSpawn, idScnParts, idPrefabInit, idResources }, - wrap_args([] (ACtxBasic& rBasic, ActiveReg_t const& rActiveIds, ACtxVehicleSpawn const& rVehicleSpawn, ACtxParts& rScnParts, ACtxPrefabInit& rPrefabInit, Resources& rResources) noexcept + .args({ idBasic, idActiveIds, idVehicleSpawn, idScnParts, idPrefabInit, idResources }) + .func([] (ACtxBasic& rBasic, ActiveReg_t const& rActiveIds, ACtxVehicleSpawn const& rVehicleSpawn, ACtxParts& rScnParts, ACtxPrefabInit& rPrefabInit, Resources& rResources) noexcept { // ActiveEnts created for welds + ActiveEnts created for vehicle prefabs std::size_t const totalEnts = rVehicleSpawn.m_newWeldToEnt.size() + rPrefabInit.m_newEnts.size(); @@ -382,10 +398,15 @@ Session setup_vehicle_spawn_newton( } })); + rBuilder.task() + .name ("aaa") + .depends_on ({}) + .fulfills ({}) + .push_to (out.m_tasks) vehicleSpawnNwt.task() = rBuilder.task().assign({tgSceneEvt, tgVsBasicInReq, tgVsWeldReq, tgNwtVhWeldEntReq, tgNwtVhHierReq, tgPfParentHierReq, tgNwtBodyMod}).data( "Add Newton physics to rigid group entities", - TopDataIds_t{ idActiveIds, idBasic, idPhys, idNwt, idVehicleSpawn, idScnParts }, - wrap_args([] (ActiveReg_t const &rActiveIds, ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ACtxVehicleSpawn const& rVehicleSpawn, ACtxParts const& rScnParts) noexcept + .args({ idActiveIds, idBasic, idPhys, idNwt, idVehicleSpawn, idScnParts }) + .func([] (ActiveReg_t const &rActiveIds, ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ACtxVehicleSpawn const& rVehicleSpawn, ACtxParts const& rScnParts) noexcept { if (rVehicleSpawn.new_vehicle_count() == 0) { @@ -580,11 +601,55 @@ static void assign_rockets( rTemp.clear(); } +// ACtxNwtWorld::ForceFactorFunc::Func_t +static void rocket_thrust_force(NewtonBody const* pBody, BodyId const body, ACtxNwtWorld const& rNwt, ACtxNwtWorld::ForceFactorFunc::UserData_t data, Vector3& rForce, Vector3& rTorque) noexcept +{ + auto const& rRocketsNwt = *reinterpret_cast (data[0]); + auto const& rMachines = *reinterpret_cast (data[1]); + auto const& rSigValFloat = *reinterpret_cast const*> (data[2]); + + auto &rBodyRockets = rRocketsNwt.m_bodyRockets[body]; + + if (rBodyRockets.size() == 0) + { + return; + } + + std::array nwtRot; // quaternion xyzw + NewtonBodyGetRotation(pBody, nwtRot.data()); + Quaternion const rot{{nwtRot[0], nwtRot[1], nwtRot[2]}, nwtRot[3]}; + + Vector3 com; + NewtonBodyGetCentreOfMass(pBody, com.data()); + + for (BodyRocket const& bodyRocket : rBodyRockets) + { + float const throttle = std::clamp(rSigValFloat[bodyRocket.m_throttleIn], 0.0f, 1.0f); + float const multiplier = rSigValFloat[bodyRocket.m_multiplierIn]; + + float const thrustMag = throttle * multiplier; + + if (thrustMag == 0.0f) + { + continue; + } + + Vector3 const offsetRel = rot.transformVector(bodyRocket.m_offset - com); + + Vector3 const direction = (rot * bodyRocket.m_rotation).transformVector(adera::gc_rocketForward); + + Vector3 const thrustForce = direction * thrustMag; + Vector3 const thrustTorque = Magnum::Math::cross(offsetRel, thrustForce); + + rForce += thrustForce; + rTorque += thrustTorque; + } +} + Session setup_rocket_thrust_newton( TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, - Session const& scnCommon, + Session const& commonScene, Session const& physics, Session const& prefabs, Session const& parts, @@ -592,8 +657,8 @@ Session setup_rocket_thrust_newton( Session const& newton, Session const& nwtFactors) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(physics, TESTAPP_PHYSICS); OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); OSP_SESSION_UNPACK_DATA(prefabs, TESTAPP_PREFABS); @@ -612,11 +677,15 @@ Session setup_rocket_thrust_newton( auto &rRocketsNwt = top_emplace< ACtxRocketsNwt >(topData, idRocketsNwt); - + rBuilder.task() + .name ("aaa") + .depends_on ({}) + .fulfills ({}) + .push_to (out.m_tasks) rocketNwt.task() = rBuilder.task().assign({tgSceneEvt, tgLinkReq, tgWeldReq, tgNwtBodyReq}).data( "Assign rockets to Newton bodies", - TopDataIds_t{ idActiveIds, idBasic, idPhys, idNwt, idScnParts, idRocketsNwt, idNwtFactors}, - wrap_args([] (ActiveReg_t const &rActiveIds, ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ACtxParts const& rScnParts, ACtxRocketsNwt& rRocketsNwt, ForceFactors_t const& rNwtFactors) noexcept + .args({ idActiveIds, idBasic, idPhys, idNwt, idScnParts, idRocketsNwt, idNwtFactors }) + .func([] (ActiveReg_t const &rActiveIds, ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ACtxParts const& rScnParts, ACtxRocketsNwt& rRocketsNwt, ForceFactors_t const& rNwtFactors) noexcept { using adera::gc_mtMagicRocket; @@ -638,52 +707,10 @@ Session setup_rocket_thrust_newton( auto &rSigValFloat = top_get< SignalValues_t > (topData, idSigValFloat); Machines &rMachines = rScnParts.m_machines; - using UserData_t = ACtxNwtWorld::ForceFactorFunc::UserData_t; + ACtxNwtWorld::ForceFactorFunc const factor { - .m_func = [] (NewtonBody const* pBody, BodyId const body, ACtxNwtWorld const& rNwt, UserData_t data, Vector3& rForce, Vector3& rTorque) noexcept - { - auto const& rRocketsNwt = *reinterpret_cast (data[0]); - auto const& rMachines = *reinterpret_cast (data[1]); - auto const& rSigValFloat = *reinterpret_cast const*> (data[2]); - - auto &rBodyRockets = rRocketsNwt.m_bodyRockets[body]; - - if (rBodyRockets.size() == 0) - { - return; - } - - std::array nwtRot; // quaternion xyzw - NewtonBodyGetRotation(pBody, nwtRot.data()); - Quaternion const rot{{nwtRot[0], nwtRot[1], nwtRot[2]}, nwtRot[3]}; - - Vector3 com; - NewtonBodyGetCentreOfMass(pBody, com.data()); - - for (BodyRocket const& bodyRocket : rBodyRockets) - { - float const throttle = std::clamp(rSigValFloat[bodyRocket.m_throttleIn], 0.0f, 1.0f); - float const multiplier = rSigValFloat[bodyRocket.m_multiplierIn]; - - float const thrustMag = throttle * multiplier; - - if (thrustMag == 0.0f) - { - continue; - } - - Vector3 const offsetRel = rot.transformVector(bodyRocket.m_offset - com); - - Vector3 const direction = (rot * bodyRocket.m_rotation).transformVector(adera::gc_rocketForward); - - Vector3 const thrustForce = direction * thrustMag; - Vector3 const thrustTorque = Magnum::Math::cross(offsetRel, thrustForce); - - rForce += thrustForce; - rTorque += thrustTorque; - } - }, + .m_func = &rocket_thrust_force, .m_userData = { &rRocketsNwt, &rMachines, &rSigValFloat } }; @@ -697,7 +724,6 @@ Session setup_rocket_thrust_newton( return rocketNwt; } - #endif } // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_newton.h b/src/test_application/activescenes/scene_newton.h index 53bd4395..85b18e12 100644 --- a/src/test_application/activescenes/scene_newton.h +++ b/src/test_application/activescenes/scene_newton.h @@ -46,7 +46,8 @@ namespace testapp::scenes osp::Session setup_newton( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& scene, + osp::Session const& commonScene, osp::Session const& physics); /** @@ -75,7 +76,7 @@ osp::Session setup_newton_force_accel( osp::Session setup_shape_spawn_newton( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& physics, osp::Session const& shapeSpawn, osp::Session const& newton, @@ -87,7 +88,7 @@ osp::Session setup_shape_spawn_newton( osp::Session setup_vehicle_spawn_newton( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& physics, osp::Session const& prefabs, osp::Session const& parts, @@ -101,7 +102,7 @@ osp::Session setup_vehicle_spawn_newton( osp::Session setup_rocket_thrust_newton( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& physics, osp::Session const& prefabs, osp::Session const& parts, diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index 3ecfe4fc..2acc51ee 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -48,139 +48,117 @@ using Corrade::Containers::arrayView; namespace testapp::scenes { -#if 0 - Session setup_physics( TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, - Session const& scnCommon) + Session const& commonScene) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - - Session physics; - OSP_SESSION_ACQUIRE_DATA(physics, topData, TESTAPP_PHYSICS); - OSP_SESSION_ACQUIRE_TAGS(physics, rTags, TESTAPP_PHYSICS); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + auto const tgCS = commonScene.get_targets(); + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_PHYSICS); + auto const tgPhy = out.create_targets(rBuilder); top_emplace< ACtxPhysics > (topData, idPhys); - // 'Per-thread' inputs fed into the physics engine. Only one here for now - //top_emplace< ACtxPhysInputs >(topData, idPhysIn); - - rBuilder.tag(tgPhysTransformReq).depend_on({tgPhysTransformMod}); - rBuilder.tag(tgPhysDel) .depend_on({tgPhysPrv}); - rBuilder.tag(tgPhysMod) .depend_on({tgPhysPrv, tgPhysDel}); - rBuilder.tag(tgPhysReq) .depend_on({tgPhysPrv, tgPhysDel, tgPhysMod}); - - physics.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgPhysDel}).data( - "Delete Physics components", - TopDataIds_t{ idPhys, idDelTotal}, - wrap_args([] (ACtxPhysics& rPhys, EntVector_t const& rDelTotal) noexcept + rBuilder.task() + .name ("Delete Physics components") + .trigger_on ({tgCS.delActiveEnt_mod}) + .fulfills ({tgCS.delActiveEnt_use, tgPhy.physics_del, tgPhy.physics_mod}) + .push_to (out.m_tasks) + .args ({ idPhys, idActiveEntDel }) + .func([] (ACtxPhysics& rPhys, ActiveEntVec_t const& rActiveEntDel) noexcept { - auto const &delFirst = std::cbegin(rDelTotal); - auto const &delLast = std::cend(rDelTotal); + SysPhysics::update_delete_phys(rPhys, rActiveEntDel.cbegin(), rActiveEntDel.cend()); + }); - SysPhysics::update_delete_phys(rPhys, delFirst, delLast); - })); - - return physics; + return out; } Session setup_shape_spawn( TopTaskBuilder& rBuilder, ArrayView const topData, - Tags& rTags, - Session const& scnCommon, + Session const& commonScene, Session const& physics, - Session const& material) + MaterialId const materialId) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); - OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); - OSP_SESSION_UNPACK_DATA(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); - - - Session shapeSpawn; - OSP_SESSION_ACQUIRE_DATA(shapeSpawn, topData, TESTAPP_SHAPE_SPAWN); - OSP_SESSION_ACQUIRE_TAGS(shapeSpawn, rTags, TESTAPP_SHAPE_SPAWN); - - top_emplace< SpawnerVec_t > (topData, idSpawner); - top_emplace< EntVector_t > (topData, idSpawnerEnts); - - rBuilder.tag(tgSpawnReq) .depend_on({tgSpawnMod}); - rBuilder.tag(tgSpawnClr) .depend_on({tgSpawnMod, tgSpawnReq}); - rBuilder.tag(tgSpawnEntReq) .depend_on({tgSpawnEntMod}); - - shapeSpawn.task() = rBuilder.task().assign({tgSceneEvt, tgSpawnReq, tgEntNew, tgSpawnEntMod}).data( - "Create entities for requested shapes to spawn", - TopDataIds_t{ idBasic, idActiveIds, idSpawner, idSpawnerEnts }, - wrap_args([] (ACtxBasic& rBasic, ActiveReg_t& rActiveIds, SpawnerVec_t& rSpawner, EntVector_t& rSpawnerEnts) noexcept + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); + auto const tgCS = commonScene .get_targets(); + auto const tgPhy = physics .get_targets(); + + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_SHAPE_SPAWN); + auto const tgShSp = out.create_targets(rBuilder); + + top_emplace< ACtxShapeSpawner > (topData, idSpawner, ACtxShapeSpawner{ .m_materialId = materialId }); + + rBuilder.task() + .name ("Create entities for requested shapes to spawn") + .trigger_on ({tgShSp.spawnRequest_mod}) + .depends_on ({ tgCS.activeEnt_del}) + .fulfills ({tgShSp.spawnRequest_use, tgShSp.spawnedEnts_mod, tgCS.activeEnt_new, tgCS.activeEnt_mod}) + .push_to (out.m_tasks) + .args ({ idBasic, idSpawner }) + .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept { - if (rSpawner.size() == 0) - { - return; - } - - rSpawnerEnts.resize(rSpawner.size() * 2); - rActiveIds.create(std::begin(rSpawnerEnts), std::end(rSpawnerEnts)); - })); - - shapeSpawn.task() = rBuilder.task().assign({tgSceneEvt, tgSpawnReq, tgSpawnEntReq, tgTransformNew, tgHierNew}).data( - "Add hierarchy and transform to spawned shapes", - TopDataIds_t{ idBasic, idActiveIds, idSpawner, idSpawnerEnts }, - wrap_args([] (ACtxBasic& rBasic, ActiveReg_t& rActiveIds, SpawnerVec_t& rSpawner, EntVector_t& rSpawnerEnts) noexcept + rSpawner.m_ents.resize(rSpawner.m_spawnRequest.size() * 2); + rBasic.m_activeIds.create(rSpawner.m_ents.begin(), rSpawner.m_ents.end()); + }); + + rBuilder.task() + .name ("Add hierarchy and transform to spawned shapes") + .trigger_on ({tgShSp.spawnRequest_mod}) + .depends_on ({tgShSp.spawnedEnts_mod}) + .fulfills ({tgShSp.spawnRequest_use, tgShSp.spawnedEnts_use, tgCS.transform_new, tgCS.transform_mod, tgCS.hier_new, tgCS.hier_mod}) + .push_to (out.m_tasks) + .args ({ idBasic, idSpawner }) + .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept { - if (rSpawner.size() == 0) - { - return; - } - - rBasic.m_scnGraph.resize(rActiveIds.capacity()); - SubtreeBuilder bldScnRoot = SysSceneGraph::add_descendants(rBasic.m_scnGraph, rSpawner.size() * 2); + rBasic.m_scnGraph.resize(rBasic.m_activeIds.capacity()); + SubtreeBuilder bldScnRoot = SysSceneGraph::add_descendants(rBasic.m_scnGraph, rSpawner.m_spawnRequest.size() * 2); - for (std::size_t i = 0; i < rSpawner.size(); ++i) + for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) { - SpawnShape const &spawn = rSpawner[i]; - ActiveEnt const root = rSpawnerEnts[i * 2]; - ActiveEnt const child = rSpawnerEnts[i * 2 + 1]; + SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; + ActiveEnt const root = rSpawner.m_ents[i * 2]; + ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; rBasic.m_transform.emplace(root, ACompTransform{osp::Matrix4::translation(spawn.m_position)}); rBasic.m_transform.emplace(child, ACompTransform{Matrix4::scaling(spawn.m_size)}); SubtreeBuilder bldRoot = bldScnRoot.add_child(root, 1); bldRoot.add_child(child); } - })); - - shapeSpawn.task() = rBuilder.task().assign({tgSceneEvt, tgSpawnReq, tgSpawnEntReq, tgMeshMod, tgDrawMod, tgMatMod}).data( - "Add mesh and material to spawned shapes", - TopDataIds_t{ idDrawing, idSpawner, idSpawnerEnts, idNMesh, idMatEnts, idMatDirty, idActiveIds}, - wrap_args([] (ACtxDrawing& rDrawing, SpawnerVec_t& rSpawner, EntVector_t& rSpawnerEnts, NamedMeshes& rNMesh, EntSet_t& rMatEnts, std::vector& rMatDirty, ActiveReg_t const& rActiveIds ) noexcept + }); + + rBuilder.task() + .name ("Add mesh and material to spawned shapes") + .trigger_on ({tgShSp.spawnRequest_mod}) + .depends_on ({tgShSp.spawnedEnts_mod}) + .fulfills ({tgShSp.spawnRequest_use, tgShSp.spawnedEnts_use, tgCS.mesh_new, tgCS.mesh_mod, tgCS.material_new, tgCS.material_mod, tgCS.drawEnt_new, tgCS.drawEnt_mod}) + .push_to (out.m_tasks) + .args ({ idBasic, idDrawing, idSpawner, idNMesh }) + .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept { - if (rSpawner.size() == 0) - { - return; - } + Material &rMat = rDrawing.m_materials[rSpawner.m_materialId]; - rDrawing.resize_active(rActiveIds.capacity()); + rDrawing.resize_active(rBasic.m_activeIds.capacity()); - for (std::size_t i = 0; i < rSpawner.size(); ++i) + for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) { - ActiveEnt const child = rSpawnerEnts[i * 2 + 1]; + ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; rDrawing.m_activeToDraw[child] = rDrawing.m_drawIds.create(); } rDrawing.resize_draw(); - bitvector_resize(rMatEnts, rDrawing.m_drawIds.capacity()); + bitvector_resize(rMat.m_ents, rDrawing.m_drawIds.capacity()); - for (std::size_t i = 0; i < rSpawner.size(); ++i) + for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) { - SpawnShape const &spawn = rSpawner[i]; - ActiveEnt const root = rSpawnerEnts[i * 2]; - ActiveEnt const child = rSpawnerEnts[i * 2 + 1]; + SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; + ActiveEnt const root = rSpawner.m_ents[i * 2]; + ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; DrawEnt const drawEnt = rDrawing.m_activeToDraw[child]; rDrawing.m_needDrawTf.set(std::size_t(root)); @@ -189,26 +167,31 @@ Session setup_shape_spawn( rDrawing.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(rNMesh.m_shapeToMesh.at(spawn.m_shape)); rDrawing.m_meshDirty.push_back(drawEnt); - rMatEnts.set(std::size_t(drawEnt)); - rMatDirty.push_back(drawEnt); + rMat.m_ents.set(std::size_t(drawEnt)); + rMat.m_dirty.push_back(drawEnt); rDrawing.m_visible.set(std::size_t(drawEnt)); rDrawing.m_drawBasic[drawEnt].m_opaque = true; } - })); - - shapeSpawn.task() = rBuilder.task().assign({tgSceneEvt, tgSpawnReq, tgSpawnEntReq, tgPhysMod}).data( - "Add physics to spawned shapes", - TopDataIds_t{ idActiveIds, idSpawner, idSpawnerEnts, idPhys }, - wrap_args([] (ActiveReg_t const &rActiveIds, SpawnerVec_t& rSpawner, EntVector_t& rSpawnerEnts, ACtxPhysics& rPhys) noexcept + }); + + rBuilder.task() + .name ("Add physics to spawned shapes") + .trigger_on ({tgShSp.spawnRequest_mod}) + .depends_on ({tgShSp.spawnedEnts_mod}) + .fulfills ({tgShSp.spawnRequest_use, tgShSp.spawnedEnts_use, tgPhy.physics_new, tgPhy.physics_mod}) + .push_to (out.m_tasks) + .args ({ idBasic, idSpawner, idPhys }) + .func([] (ACtxBasic const& rBasic, ACtxShapeSpawner& rSpawner, ACtxPhysics& rPhys) noexcept { - rPhys.m_hasColliders.ints().resize(rActiveIds.vec().capacity()); - rPhys.m_shape.resize(rActiveIds.capacity()); - for (std::size_t i = 0; i < rSpawner.size(); ++i) + rPhys.m_hasColliders.ints().resize(rBasic.m_activeIds.vec().capacity()); + rPhys.m_shape.resize(rBasic.m_activeIds.capacity()); + + for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) { - SpawnShape const &spawn = rSpawner[i]; - ActiveEnt const root = rSpawnerEnts[i * 2]; - ActiveEnt const child = rSpawnerEnts[i * 2 + 1]; + SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; + ActiveEnt const root = rSpawner.m_ents[i * 2]; + ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; rPhys.m_hasColliders.set(std::size_t(root)); if (spawn.m_mass != 0.0f) @@ -223,31 +206,36 @@ Session setup_shape_spawn( rPhys.m_shape[std::size_t(child)] = spawn.m_shape; rPhys.m_colliderDirty.push_back(child); } - })); - - shapeSpawn.task() = rBuilder.task().assign({tgSceneEvt, tgSpawnClr}).data( - "Clear Shape Spawning vector after use", - TopDataIds_t{ idSpawner}, - wrap_args([] (SpawnerVec_t& rSpawner) noexcept + }); + + rBuilder.task() + .name ("Clear Shape Spawning vector after use") + .trigger_on ({tgShSp.spawnRequest_use}) + .fulfills ({tgShSp.spawnRequest_clr}) + .push_to (out.m_tasks) + .args ({ idSpawner }) + .func([] (ACtxShapeSpawner& rSpawner) noexcept { - rSpawner.clear(); - })); + rSpawner.m_spawnRequest.clear(); + }); - return shapeSpawn; + return out; } +#if 0 + Session setup_prefabs( TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, - Session const& scnCommon, + Session const& commonScene, Session const& physics, Session const& material, TopDataId const idResources) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(physics, TESTAPP_PHYSICS); OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); @@ -369,12 +357,12 @@ Session setup_bounds( TopTaskBuilder& rBuilder, ArrayView const topData, Tags& rTags, - Session const& scnCommon, + Session const& commonScene, Session const& physics, Session const& shapeSpawn) { - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); OSP_SESSION_UNPACK_DATA(shapeSpawn, TESTAPP_SHAPE_SPAWN); OSP_SESSION_UNPACK_TAGS(shapeSpawn, TESTAPP_SHAPE_SPAWN); @@ -443,6 +431,10 @@ Session setup_bounds( bounds.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgBoundsSetDel}).data( "Delete bounds components", TopDataIds_t{idBounds, idDelTotal}, delete_ent_set); +// for (osp::active::ActiveEnt const ent : rDelTotal) +// { +// rSet.reset(std::size_t(ent)); +// } return bounds; } diff --git a/src/test_application/activescenes/scene_physics.h b/src/test_application/activescenes/scene_physics.h index 0b9f29f7..073d5310 100644 --- a/src/test_application/activescenes/scene_physics.h +++ b/src/test_application/activescenes/scene_physics.h @@ -45,7 +45,12 @@ struct SpawnShape osp::phys::EShape m_shape; }; -using SpawnerVec_t = std::vector; +struct ACtxShapeSpawner +{ + std::vector m_spawnRequest; + osp::active::ActiveEntVec_t m_ents; + osp::active::MaterialId m_materialId; +}; /** * @brief Physical properties for entities and generic Physics interface @@ -55,7 +60,7 @@ using SpawnerVec_t = std::vector; osp::Session setup_physics( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon); + osp::Session const& commonScene); /** * @brief Newton Dynamics physics integration @@ -63,7 +68,7 @@ osp::Session setup_physics( osp::Session setup_newton_physics( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& physics); /** @@ -72,9 +77,9 @@ osp::Session setup_newton_physics( osp::Session setup_shape_spawn( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& scene, osp::Session const& physics, - osp::Session const& material); + osp::active::MaterialId materialId); /** * @brief Queues and logic for spawning Prefab resources @@ -82,7 +87,7 @@ osp::Session setup_shape_spawn( osp::Session setup_prefabs( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& physics, osp::Session const& material, osp::TopDataId idResources); @@ -93,7 +98,7 @@ osp::Session setup_prefabs( osp::Session setup_bounds( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& physics, osp::Session const& shapeSpawn); diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 5878f0e5..5ffc35ca 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -89,7 +89,7 @@ Session setup_magnum( Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_MAGNUM); auto const tgMgn = out.create_targets(rBuilder); - out.m_cleanupTgt = tgMgn.cleanup; + out.m_cleanup = tgMgn.cleanup; // Order-dependent; ActiveApplication construction starts OpenGL context, needed by RenderGL /* unused */ top_emplace(topData, idActiveApp, args, rUserInput); @@ -98,10 +98,10 @@ Session setup_magnum( SysRenderGL::setup_context(rRenderGl); rBuilder.task() - .push_to(out.m_tasks) - .depends_on({tgMgn.cleanup}) - .name("Clean up Magnum renderer") - .data({ idResources, idRenderGl}) + .name ("Clean up Magnum renderer") + .depends_on ({tgMgn.cleanup}) + .push_to (out.m_tasks) + .args ({ idResources, idRenderGl}) .func([] (Resources& rResources, RenderGL& rRenderGl) noexcept { SysRenderGL::clear_resource_owners(rRenderGl, rResources); @@ -111,23 +111,26 @@ Session setup_magnum( return out; } -#if 0 - Session setup_scene_renderer( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& windowApp, Session const& magnum, - Session const& scnCommon, + Session const& scene, + Session const& commonScene, TopDataId const idResources) { - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(magnum, TESTAPP_APP_MAGNUM); - OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); + OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); + auto const tgWin = windowApp .get_targets(); + auto const tgScn = scene .get_targets(); + auto const tgCS = commonScene .get_targets(); + auto const tgMgn = magnum .get_targets(); - Session renderer; - OSP_SESSION_ACQUIRE_DATA(renderer, topData, TESTAPP_COMMON_RENDERER); - OSP_SESSION_ACQUIRE_TAGS(renderer, rTags, TESTAPP_COMMON_RENDERER); + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_COMMON_RENDERER); + auto const tgSR = out.create_targets(rBuilder); top_emplace< ACtxSceneRenderGL > (topData, idScnRender); top_emplace< RenderGroup > (topData, idGroupFwd); @@ -138,63 +141,63 @@ Session setup_scene_renderer( rCamera.m_near = 1.0f; rCamera.m_fov = Magnum::Deg(45.0f); - rBuilder.tag(tgDrawGlMod) .depend_on({tgDrawGlDel}); - rBuilder.tag(tgDrawGlReq) .depend_on({tgDrawGlDel, tgDrawGlMod}); - rBuilder.tag(tgMeshGlReq) .depend_on({tgMeshGlMod}); - rBuilder.tag(tgTexGlReq) .depend_on({tgTexGlMod}); - rBuilder.tag(tgEntTexReq) .depend_on({tgEntTexMod}); - rBuilder.tag(tgEntMeshReq) .depend_on({tgEntMeshMod}); - rBuilder.tag(tgCameraReq) .depend_on({tgCameraMod}); - rBuilder.tag(tgGroupFwdMod) .depend_on({tgGroupFwdDel}); - rBuilder.tag(tgGroupFwdReq) .depend_on({tgGroupFwdDel, tgGroupFwdMod}); - rBuilder.tag(tgBindFboReq) .depend_on({tgBindFboMod}); - rBuilder.tag(tgFwdRenderReq) .depend_on({tgFwdRenderMod}); - rBuilder.tag(tgDrawTransformNew) .depend_on({tgDrawTransformDel}); - rBuilder.tag(tgDrawTransformMod) .depend_on({tgDrawTransformDel, tgDrawTransformNew}); - rBuilder.tag(tgDrawTransformReq) .depend_on({tgDrawTransformDel, tgDrawTransformNew, tgDrawTransformMod}); - - renderer.task() = rBuilder.task().assign({tgSyncEvt, tgTexGlMod, tgMeshGlMod, tgDrawGlMod, tgDrawReq}).data( - "Resize Scene Render containers to fit drawable entities", - TopDataIds_t{ idDrawing, idScnRender}, - wrap_args([] (ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept + rBuilder.task() + .name ("Resize Scene Render containers to fit drawable entities") + .trigger_on ({tgCS.drawEnt_mod}) + .fulfills ({tgSR.scnRender_new, tgSR.scnRender_mod, tgSR.drawTransform_new, tgSR.drawTransform_mod, + tgSR.entTexture_new, tgSR.entTexture_mod, tgSR.entMesh_new, tgSR.entMesh_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing, idScnRender}) + .func([] (ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept { std::size_t const capacity = rDrawing.m_drawIds.capacity(); rScnRender.m_drawTransform .resize(capacity); rScnRender.m_diffuseTexId .resize(capacity); rScnRender.m_meshId .resize(capacity); - })); + }); - renderer.task() = rBuilder.task().assign({tgSyncEvt, tgGlUse, tgTexGlMod, tgTexGlMod}).data( - "Synchronize used mesh and texture Resources with GL", - TopDataIds_t{ idDrawingRes, idResources, idRenderGl}, - wrap_args([] (ACtxDrawingRes const& rDrawingRes, osp::Resources& rResources, RenderGL& rRenderGl) noexcept + rBuilder.task() + .name ("Synchronize used mesh and texture Resources with GL") + .trigger_on ({tgCS.mesh_mod, tgCS.texture_mod}) + .fulfills ({tgCS.mesh_use, tgCS.texture_use, tgMgn.meshGL_mod, tgMgn.textureGL_mod}) + .push_to (out.m_tasks) + .args ({ idDrawingRes, idResources, idRenderGl }) + .func([] (ACtxDrawingRes const& rDrawingRes, osp::Resources& rResources, RenderGL& rRenderGl) noexcept { SysRenderGL::sync_scene_resources(rDrawingRes, rResources, rRenderGl); - })); + }); - renderer.task() = rBuilder.task().assign({tgSyncEvt, tgTexGlReq, tgEntTexMod}).data( - "Assign GL textures to entities with scene textures", - TopDataIds_t{ idDrawing, idDrawingRes, idScnRender, idRenderGl}, - wrap_args([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept + rBuilder.task() + .name ("Assign GL textures to entities with scene textures") + .trigger_on ({tgCS.texture_mod}) + .depends_on ({ tgMgn.textureGL_mod, tgCS.drawEnt_mod, tgSR.entTexture_new}) + .fulfills ({tgCS.texture_use, tgMgn.textureGL_use, tgCS.drawEnt_use, tgSR.entTexture_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) + .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept { SysRenderGL::assign_textures(rDrawing.m_diffuseTex, rDrawingRes.m_texToRes, rDrawing.m_diffuseDirty, rScnRender.m_diffuseTexId, rRenderGl); - })); + }); - renderer.task() = rBuilder.task().assign({tgSyncEvt, tgTexGlReq, tgEntMeshMod, tgMeshReq}).data( - "Assign GL meshes to entities with scene meshes", - TopDataIds_t{ idDrawing, idDrawingRes, idScnRender, idRenderGl}, - wrap_args([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept + rBuilder.task() + .name ("Assign GL meshes to entities with scene meshes") + .trigger_on ({tgCS.mesh_mod}) + .depends_on ({ tgMgn.meshGL_mod, tgCS.drawEnt_mod, tgSR.entMesh_new}) + .fulfills ({tgCS.mesh_use, tgMgn.meshGL_use, tgCS.drawEnt_use, tgSR.entMesh_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) + .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept { SysRenderGL::assign_meshes(rDrawing.m_mesh, rDrawingRes.m_meshToRes, rDrawing.m_meshDirty, rScnRender.m_meshId, rRenderGl); - })); - - // TODO: Separate forward renderer into different tasks to allow other - // rendering techniques + }); - renderer.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboMod}).data( - "Bind Offscreen FBO", - TopDataIds_t{ idDrawing, idRenderGl, idGroupFwd, idCamera}, - wrap_args([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept + rBuilder.task() + .name ("Bind and display off-screen FBO") + .trigger_on ({tgWin.render}) + .fulfills ({tgSR.fboRender}) + .push_to (out.m_tasks) + .args ({ idDrawing, idRenderGl, idGroupFwd, idCamera }) + .func([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept { using Magnum::GL::Framebuffer; using Magnum::GL::FramebufferClear; @@ -202,88 +205,88 @@ Session setup_scene_renderer( Framebuffer &rFbo = rRenderGl.m_fbo; rFbo.bind(); + Magnum::GL::Texture2D &rFboColor = rRenderGl.m_texGl.get(rRenderGl.m_fboColor); + SysRenderGL::display_texture(rRenderGl, rFboColor); + // Clear it rFbo.clear( FramebufferClear::Color | FramebufferClear::Depth | FramebufferClear::Stencil); - })); + }); - renderer.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderReq}).data( - "Display Offscreen FBO", - TopDataIds_t{ idDrawing, idRenderGl, idGroupFwd, idCamera}, - wrap_args([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept + rBuilder.task() + .name ("Calculate draw transforms") + .trigger_on ({tgSR.fboRender}) + .depends_on ({tgSR.drawTransform_new, tgCS.hier_mod, tgCS.drawEnt_mod, tgCS.transform_mod}) + .fulfills ({tgSR.drawTransform_mod, tgCS.hier_use, tgCS.drawEnt_use, tgCS.transform_use}) + .push_to (out.m_tasks) + .args ({ idBasic, idDrawing, idScnRender }) + .func([] (ACtxBasic const& rBasic, ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept { - Magnum::GL::Texture2D &rFboColor = rRenderGl.m_texGl.get(rRenderGl.m_fboColor); - SysRenderGL::display_texture(rRenderGl, rFboColor); - })); + auto rootChildren = SysSceneGraph::children(rBasic.m_scnGraph); + SysRender::update_draw_transforms( + rBasic .m_scnGraph, + rDrawing .m_activeToDraw, + rBasic .m_transform, + rScnRender .m_drawTransform, + rDrawing .m_needDrawTf, + rootChildren.begin(), + rootChildren.end()); + }); - renderer.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgBindFboReq, tgFwdRenderMod, tgDrawTransformReq, tgGroupFwdReq, tgDrawGlReq, tgCameraReq, tgEntTexMod, tgEntMeshMod}).data( - "Render Entities", - TopDataIds_t{ idDrawing, idRenderGl, idGroupFwd, idCamera}, - wrap_args([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept + rBuilder.task() + .name ("Render Entities") + .trigger_on ({tgSR.fboRender}) + .depends_on ({ tgSR.scnRender_mod, tgSR.group_mod, tgSR.groupEnts_mod, tgSR.drawTransform_mod, tgSR.camera_mod, tgSR.entMesh_mod, tgSR.entTexture_mod, tgMgn.meshGL_mod, tgMgn.textureGL_mod, tgCS.drawEnt_mod}) + .fulfills ({tgSR.fboRenderDone, tgSR.scnRender_use, tgSR.group_use, tgSR.groupEnts_use, tgSR.drawTransform_use, tgSR.camera_use, tgSR.entMesh_use, tgSR.entTexture_use, tgMgn.meshGL_use, tgMgn.textureGL_use, tgCS.drawEnt_use}) + .push_to (out.m_tasks) + .args ({ idDrawing, idRenderGl, idGroupFwd, idCamera }) + .func([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera, WorkerContext ctx) noexcept { ViewProjMatrix viewProj{rCamera.m_transform.inverted(), rCamera.perspective()}; // Forward Render fwd_opaque group to FBO SysRenderGL::render_opaque(rGroupFwd, rDrawing.m_visible, viewProj); - })); - - renderer.task() = rBuilder.task().assign({tgSyncEvt, tgDelTotalReq, tgDrawGlDel}).data( - "Delete GL components", - TopDataIds_t{ idScnRender, idDelTotal}, - wrap_args([] (ACtxSceneRenderGL& rScnRender, EntVector_t const& rDelTotal) noexcept - { - SysRenderGL::update_delete(rScnRender, std::cbegin(rDelTotal), std::cend(rDelTotal)); - })); - - renderer.task() = rBuilder.task().assign({tgSyncEvt, tgHierReq, tgTransformReq, tgDrawTransformMod}).data( - "Calculate draw transforms", - TopDataIds_t{ idBasic, idDrawing, idScnRender}, - wrap_args([] (ACtxBasic const& rBasic, ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept - { - auto rootChildren = SysSceneGraph::children(rBasic.m_scnGraph); - SysRender::update_draw_transforms( - rBasic.m_scnGraph, - rDrawing.m_activeToDraw, - rBasic.m_transform, - rScnRender.m_drawTransform, - rDrawing.m_needDrawTf, - std::begin(rootChildren), - std::end(rootChildren)); - })); + }); - renderer.task() = rBuilder.task().assign({tgSyncEvt, tgGroupFwdDel, tgDelDrawEntReq}).data( - "Delete entities from render groups", - TopDataIds_t{ idDrawing, idGroupFwd, idDelDrawEnts}, - wrap_args([] (ACtxDrawing const& rDrawing, RenderGroup& rGroup, DrawEntVector_t const& rDelDrawEnts) noexcept + rBuilder.task() + .name ("Delete entities from render groups") + .trigger_on ({tgCS.delDrawEnt_mod}) + .fulfills ({tgCS.delDrawEnt_use, tgSR.groupEnts_del, tgSR.groupEnts_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing, idGroupFwd, idDrawEntDel }) + .func([] (ACtxDrawing const& rDrawing, RenderGroup& rGroup, DrawEntVec_t const& rDrawEntDel) noexcept { - for (DrawEnt const drawEnt : rDelDrawEnts) + for (DrawEnt const drawEnt : rDrawEntDel) { rGroup.m_entities.remove(drawEnt); } - })); + }); - return renderer; + return out; } Session setup_shader_visualizer( TopTaskBuilder& rBuilder, ArrayView const topData, Session const& magnum, - Session const& scnCommon, + Session const& commonScene, Session const& scnRender, - Session const& material) + MaterialId const materialId) { - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); - OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(scnRender, TESTAPP_DATA_COMMON_RENDERER); + OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); + auto const tgCS = commonScene .get_targets(); + auto const tgSR = scnRender .get_targets(); + auto const tgMgn = magnum .get_targets(); + auto &rScnRender = top_get< ACtxSceneRenderGL > (topData, idScnRender); auto &rRenderGl = top_get< RenderGL > (topData, idRenderGl); - Session shVisual; - OSP_SESSION_ACQUIRE_DATA(shVisual, topData, TESTAPP_SHADER_VISUALIZER) + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_SHADER_VISUALIZER) auto &rDrawVisual = top_emplace< ACtxDrawMeshVisualizer >(topData, idDrawShVisual); + rDrawVisual.m_materialId = materialId; rDrawVisual.m_shader = MeshVisualizer{ MeshVisualizer::Configuration{}.setFlags(MeshVisualizer::Flag::Wireframe) }; rDrawVisual.assign_pointers(rScnRender, rRenderGl); @@ -292,46 +295,42 @@ Session setup_shader_visualizer( rDrawVisual.m_shader.setWireframeColor({0.7f, 0.5f, 0.7f, 1.0f}); rDrawVisual.m_shader.setColor({0.2f, 0.1f, 0.5f, 1.0f}); - if (material.m_dataIds.empty()) + if (materialId == lgrn::id_null()) { - return shVisual; + return out; } - OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); - OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); - - shVisual.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgGroupFwdMod}).data( - "Sync MeshVisualizer shader entities", - TopDataIds_t{ idMatDirty, idMatEnts, idGroupFwd, idDrawShVisual}, - wrap_args([] (std::vector const& rMatDirty, EntSet_t const& rMatEnts, RenderGroup& rGroup, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept - { - sync_visualizer(std::cbegin(rMatDirty), std::cend(rMatDirty), rMatEnts, rGroup.m_entities, rDrawShVisual); - })); - shVisual.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgTransformReq, tgDrawTransformNew}).data( - "Add draw transforms to mesh visualizer", - TopDataIds_t{ idMatDirty, idScnRender}, - wrap_args([] (std::vector const& rMatDirty, ACtxSceneRenderGL& rScnRender) noexcept + rBuilder.task() + .name ("Sync MeshVisualizer shader entities") + .trigger_on ({tgCS.material_mod}) + .depends_on ({ tgSR.groupEnts_mod}) + .fulfills ({tgCS.material_use, tgSR.groupEnts_use, tgSR.group_mod}) + .push_to (out.m_tasks) + .args ({ idDrawing, idGroupFwd, idDrawShVisual}) + .func([] (ACtxDrawing const& rDrawing, RenderGroup& rGroupFwd, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept { - //SysRender::assure_draw_transforms(rScnRender.m_drawTransform, std::cbegin(rMatDirty), std::cend(rMatDirty)); - })); - + Material const &rMat = rDrawing.m_materials[rDrawShVisual.m_materialId]; + sync_visualizer(rMat.m_dirty.begin(), rMat.m_dirty.end(), rMat.m_ents, rGroupFwd.m_entities, rDrawShVisual); + }); - return shVisual; + return out; } +#if 0 + Session setup_shader_flat( TopTaskBuilder& rBuilder, ArrayView const topData, Session const& magnum, - Session const& scnCommon, + Session const& commonScene, Session const& scnRender, Session const& material) { using osp::shader::Flat; - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); @@ -369,14 +368,14 @@ Session setup_shader_phong( TopTaskBuilder& rBuilder, ArrayView const topData, Session const& magnum, - Session const& scnCommon, + Session const& commonScene, Session const& scnRender, Session const& material) { using osp::shader::Phong; - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); @@ -422,7 +421,7 @@ Session setup_thrust_indicators( TopTaskBuilder& rBuilder, ArrayView const topData, Session const& magnum, - Session const& scnCommon, + Session const& commonScene, Session const& parts, Session const& signalsFloat, Session const& scnRender, @@ -442,8 +441,8 @@ Session setup_thrust_indicators( OSP_SESSION_UNPACK_TAGS(magnum, TESTAPP_APP_MAGNUM); OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) @@ -572,7 +571,7 @@ Session setup_cursor( TopTaskBuilder& rBuilder, ArrayView const topData, Session const& magnum, - Session const& scnCommon, + Session const& commonScene, Session const& scnRender, Session const& cameraCtrl, Session const& shFlat, @@ -583,8 +582,8 @@ Session setup_cursor( OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(cameraCtrl, TESTAPP_CAMERA_CTRL); OSP_SESSION_UNPACK_TAGS(cameraCtrl, TESTAPP_CAMERA_CTRL); OSP_SESSION_UNPACK_DATA(shFlat, TESTAPP_SHADER_FLAT); @@ -639,7 +638,7 @@ Session setup_uni_test_planets_renderer( ArrayView const topData, Session const& magnum, Session const& scnRender, - Session const& scnCommon, + Session const& commonScene, Session const& cameraCtrl, Session const& visualizer, Session const& uniCore, @@ -652,8 +651,8 @@ Session setup_uni_test_planets_renderer( OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(cameraCtrl, TESTAPP_CAMERA_CTRL); OSP_SESSION_UNPACK_TAGS(cameraCtrl, TESTAPP_CAMERA_CTRL); OSP_SESSION_UNPACK_DATA(visualizer, TESTAPP_SHADER_VISUALIZER); diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index 38ff46d3..a5085059 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -26,6 +26,8 @@ #include "scenarios.h" +#include + #include "../ActiveApplication.h" @@ -49,8 +51,10 @@ osp::Session setup_magnum( osp::Session setup_scene_renderer( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& windowApp, osp::Session const& magnum, osp::Session const& scene, + osp::Session const& commonScene, osp::TopDataId idResources); /** @@ -62,7 +66,7 @@ osp::Session setup_shader_visualizer( osp::Session const& magnum, osp::Session const& scene, osp::Session const& scnRender, - osp::Session const& material); + osp::active::MaterialId materialId = lgrn::id_null()); /** * @brief Magnum Flat shader and optional material for drawing ActiveEnts with it @@ -93,7 +97,7 @@ osp::Session setup_thrust_indicators( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, osp::Session const& magnum, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& parts, osp::Session const& signalsFloat, osp::Session const& scnRender, @@ -109,7 +113,7 @@ osp::Session setup_cursor( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, osp::Session const& magnum, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& scnRender, osp::Session const& cameraCtrl, osp::Session const& shFlat, @@ -124,7 +128,7 @@ osp::Session setup_uni_test_planets_renderer( osp::ArrayView topData, osp::Session const& magnum, osp::Session const& scnRender, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& cameraCtrl, osp::Session const& visualizer, osp::Session const& uniCore, diff --git a/src/test_application/activescenes/scene_vehicles.cpp b/src/test_application/activescenes/scene_vehicles.cpp index 40f2ca42..5b5d862b 100644 --- a/src/test_application/activescenes/scene_vehicles.cpp +++ b/src/test_application/activescenes/scene_vehicles.cpp @@ -56,10 +56,10 @@ namespace testapp::scenes Session setup_parts( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& scnCommon, + Session const& commonScene, TopDataId const idResources) { - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); Session parts; OSP_SESSION_ACQUIRE_DATA(parts, topData, TESTAPP_PARTS); @@ -143,10 +143,10 @@ Session setup_parts( Session setup_signals_float( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& scnCommon, + Session const& commonScene, Session const& parts) { - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); @@ -239,11 +239,11 @@ TopTaskFunc_t gen_allocate_mach_bitsets() Session setup_mach_rocket( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& scnCommon, + Session const& commonScene, Session const& parts, Session const& signalsFloat) { - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); @@ -267,11 +267,11 @@ Session setup_mach_rocket( Session setup_mach_rcsdriver( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& scnCommon, + Session const& commonScene, Session const& parts, Session const& signalsFloat) { - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); @@ -356,9 +356,9 @@ Session setup_mach_rcsdriver( Session setup_vehicle_spawn( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& scnCommon) + Session const& commonScene) { - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); Session vehicleSpawn; OSP_SESSION_ACQUIRE_DATA(vehicleSpawn, topData, TESTAPP_VEHICLE_SPAWN); @@ -387,15 +387,15 @@ Session setup_vehicle_spawn( Session setup_vehicle_spawn_vb( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& scnCommon, + Session const& commonScene, Session const& prefabs, Session const& parts, Session const& vehicleSpawn, Session const& signalsFloat, TopDataId const idResources) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(prefabs, TESTAPP_PREFABS); OSP_SESSION_UNPACK_TAGS(prefabs, TESTAPP_PREFABS); OSP_SESSION_UNPACK_DATA(vehicleSpawn, TESTAPP_VEHICLE_SPAWN); @@ -958,10 +958,10 @@ void add_rcs_block(VehicleBuilder& rBuilder, VehicleBuilder::WeldVec_t& rWeldTo, Session setup_test_vehicles( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& scnCommon, + Session const& commonScene, TopDataId const idResources) { - OSP_SESSION_UNPACK_TAGS(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); Session testVehicles; OSP_SESSION_ACQUIRE_DATA(testVehicles, topData, TESTAPP_TEST_VEHICLES); @@ -1074,12 +1074,12 @@ struct VehicleTestControls Session setup_vehicle_control( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& scnCommon, + Session const& commonScene, Session const& parts, Session const& signalsFloat, Session const& app) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); @@ -1207,13 +1207,13 @@ Session setup_camera_vehicle( TopTaskBuilder& rBuilder, [[maybe_unused]] ArrayView const topData, Session const& app, - Session const& scnCommon, + Session const& commonScene, Session const& parts, Session const& physics, Session const& camera, Session const& vehicleControl) { - OSP_SESSION_UNPACK_DATA(scnCommon, TESTAPP_COMMON_SCENE); + OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); OSP_SESSION_UNPACK_TAGS(app, TESTAPP_APP); diff --git a/src/test_application/activescenes/scene_vehicles.h b/src/test_application/activescenes/scene_vehicles.h index 987a7a52..5ea998e5 100644 --- a/src/test_application/activescenes/scene_vehicles.h +++ b/src/test_application/activescenes/scene_vehicles.h @@ -37,7 +37,7 @@ namespace testapp::scenes osp::Session setup_parts( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::TopDataId const idResources); /** @@ -72,7 +72,7 @@ osp::Session setup_parts( osp::Session setup_signals_float( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& parts); /** @@ -83,7 +83,7 @@ osp::Session setup_signals_float( osp::Session setup_mach_rocket( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& parts, osp::Session const& signalsFloat); @@ -93,7 +93,7 @@ osp::Session setup_mach_rocket( osp::Session setup_mach_rcsdriver( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& parts, osp::Session const& signalsFloat); @@ -106,7 +106,7 @@ osp::Session setup_mach_rcsdriver( osp::Session setup_vehicle_spawn( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon); + osp::Session const& commonScene); /** * @brief Support VehicleBuilder data to be used to spawn vehicles @@ -114,7 +114,7 @@ osp::Session setup_vehicle_spawn( osp::Session setup_vehicle_spawn_vb( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& prefabs, osp::Session const& parts, osp::Session const& vehicleSpawn, @@ -127,7 +127,7 @@ osp::Session setup_vehicle_spawn_vb( osp::Session setup_test_vehicles( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::TopDataId const idResources); /** @@ -136,7 +136,7 @@ osp::Session setup_test_vehicles( osp::Session setup_vehicle_control( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& parts, osp::Session const& signalsFloat, osp::Session const& app); @@ -148,7 +148,7 @@ osp::Session setup_camera_vehicle( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, osp::Session const& app, - osp::Session const& scnCommon, + osp::Session const& commonScene, osp::Session const& parts, osp::Session const& physics, osp::Session const& camera, diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index d7c32b03..7a83d1a4 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -35,6 +35,8 @@ #include #include +#include + #include #include @@ -55,9 +57,11 @@ #include #include +#include #include #include #include + #include #include @@ -262,11 +266,18 @@ void start_magnum_async() // Setup renderer sessions + g_testApp.m_rendererSetup(g_testApp); + g_testApp.m_exec.resize(g_testApp.m_tasks); g_testApp.m_graph.reset(); g_testApp.m_graph.emplace(osp::make_exec_graph(g_testApp.m_tasks, {&g_testApp.m_renderer.m_edges, &g_testApp.m_scene.m_edges})); + std::ofstream dot; + dot.open("tasks.gv"); + osp::write_dot_graph(dot, g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_taskData); + dot.close(); - g_testApp.m_rendererSetup(g_testApp); + enqueue_dirty(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_exec); + top_run_blocking(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_taskData, g_testApp.m_topData, g_testApp.m_exec); // Starts the main loop. This function is blocking, and will only return // once the window is closed. See ActiveApplication::drawEvent diff --git a/src/test_application/testapp.cpp b/src/test_application/testapp.cpp index 7e3045af..e044c701 100644 --- a/src/test_application/testapp.cpp +++ b/src/test_application/testapp.cpp @@ -24,11 +24,11 @@ */ #include "testapp.h" -#include - #include #include +#include + namespace testapp { diff --git a/src/test_application/testapp.h b/src/test_application/testapp.h index dfcdc926..5dbc2a77 100644 --- a/src/test_application/testapp.h +++ b/src/test_application/testapp.h @@ -31,7 +31,6 @@ #include #include -#include #include #include @@ -45,19 +44,6 @@ using RendererSetupFunc_t = void(*)(TestApp&); using SceneSetupFunc_t = RendererSetupFunc_t(*)(TestApp&); -// TopData stores most application state, addressed using a TopDataId - -// Sessions bundle together and own TopDataIds, TopTaskIds, and TagsIds -// Sessions intend to add support for something to exist in the world -// eg, Adding support for physics or supporting a certain shader - -// Current execution state of TopTasks - -// TopTasks and are organized with Tags to form task graphs and events. -// Each TopTask is given a vector of TopDataIds its allowed to access -// Called when openning a Magnum Application - - struct TestAppTasks { std::vector m_topData; diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 1a4c4db1..02324c64 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -102,7 +102,7 @@ TEST(Tasks, BasicParallelSingleThreaded) // Clear vector before use builder.task() - .depends_on({inputIn}) + .trigger_on({inputIn}) .fulfills({vecClear}) .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t { @@ -114,7 +114,7 @@ TEST(Tasks, BasicParallelSingleThreaded) for (int i = 0; i < sc_pusherTaskCount; ++i) { builder.task() - .depends_on({vecClear}) + .trigger_on({vecClear}) .fulfills({vecReady}) .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t { @@ -192,7 +192,7 @@ TEST(Tasks, BasicSingleThreaded) // Two tasks calculate forces needed by the physics update TaskId const taskA = builder.task() - .depends_on({tgt.timeIn}) + .trigger_on({tgt.timeIn}) .fulfills({tgt.forces}) .func( [] (TestWorld& rWorld) -> FulfillDirty_t { @@ -200,7 +200,7 @@ TEST(Tasks, BasicSingleThreaded) return {{0b01}}; }); TaskId const taskB = builder.task() - .depends_on({tgt.timeIn}) + .trigger_on({tgt.timeIn}) .fulfills({tgt.forces}) .func([] (TestWorld& rWorld) -> FulfillDirty_t { @@ -210,7 +210,8 @@ TEST(Tasks, BasicSingleThreaded) // Main Physics update TaskId const taskC = builder.task() - .depends_on({tgt.timeIn, tgt.forces}) + .trigger_on({tgt.timeIn}) + .depends_on({tgt.forces}) .fulfills({tgt.positions}) .func([] (TestWorld& rWorld) -> FulfillDirty_t { @@ -223,7 +224,8 @@ TEST(Tasks, BasicSingleThreaded) // Draw things moved by physics update. If 'updWorld' wasn't enqueued, then // this will still run, as no 'needPhysics' tasks are incomplete TaskId const taskD = builder.task() - .depends_on({tgt.positions, tgt.renderRequestIn}) + .trigger_on({tgt.renderRequestIn}) + .depends_on({tgt.positions}) .fulfills({tgt.renderDoneOut}) .func([] (TestWorld& rWorld) -> FulfillDirty_t { @@ -235,7 +237,7 @@ TEST(Tasks, BasicSingleThreaded) // Draw things unrelated to physics. This is allowed to be the first task // to run TaskId const taskE = builder.task() - .depends_on({tgt.renderRequestIn}) + .trigger_on({tgt.renderRequestIn}) .fulfills({tgt.renderDoneOut}) .func([] (TestWorld& rWorld) -> FulfillDirty_t { From de6ad81dc8d655fe66eebd8051c551a1a070fab3 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 4 Jun 2023 19:01:18 -0700 Subject: [PATCH 16/35] Rewrite Task systems into "Pipelines" --- src/osp/tasks/builder.h | 76 +++++---- src/osp/tasks/execute.cpp | 271 ++++++++++++++++++++++++--------- src/osp/tasks/execute.h | 79 ++++++++-- src/osp/tasks/sequence.h | 0 src/osp/tasks/task_types.h | 34 +++++ src/osp/tasks/tasks.cpp | 174 ++++++++++++--------- src/osp/tasks/tasks.h | 110 ++++++++----- src/osp/tasks/top_execute.cpp | 6 +- src/osp/tasks/top_execute.h | 8 +- src/osp/tasks/top_session.cpp | 2 +- src/osp/tasks/top_session.h | 2 +- src/test_application/testapp.h | 2 +- test/tasks/main.cpp | 190 ++++++++++++----------- 13 files changed, 636 insertions(+), 318 deletions(-) create mode 100644 src/osp/tasks/sequence.h create mode 100644 src/osp/tasks/task_types.h diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index b6a8855b..417a0a8b 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -43,6 +43,11 @@ namespace osp template struct TaskBuilderBase { + constexpr TaskBuilderBase(Tasks &rTasks, TaskEdges &rEdges) noexcept + : m_rTasks{rTasks} + , m_rEdges{rEdges} + { } + TASKREF_T task() { TaskId const taskId = m_rTasks.m_taskIds.create(); @@ -61,23 +66,23 @@ struct TaskBuilderBase } template - TGT_STRUCT_T create_targets() + TGT_STRUCT_T create_pipelines() { - static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); - constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); + static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineId) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineId); - std::array out; + std::array out; - m_rTasks.m_targetIds.create(out.begin(), out.end()); + m_rTasks.m_pipelineIds.create(out.begin(), out.end()); return reinterpret_cast(*out.data()); } template - std::array create_targets() + std::array create_stateloops() { - std::array out; - m_rTasks.m_targetIds.create(out.begin(), out.end()); + std::array out; + m_rTasks.m_pipelineIds.create(out.begin(), out.end()); return out; } @@ -86,6 +91,17 @@ struct TaskBuilderBase }; // class TaskBuilderBase +struct PipelineSpec +{ + template + PipelineSpec(PipelineDef const pipeline, STAGE_ENUM_T const stage) + : m_pipeline{pipeline} + , m_stage{StageId(stage)} + { } + + PipelineId m_pipeline; + StageId m_stage; +}; template struct TaskRefBase @@ -99,46 +115,48 @@ struct TaskRefBase constexpr Tasks & tasks() noexcept { return m_rBuilder.m_rTasks; } - TASKREF_T& trigger_on(ArrayView const targets) noexcept + template + TASKREF_T& add_edges(std::vector& rContainer, RANGE_T const& add) { - for (TargetId const target : targets) + for (PipelineSpec const spec : add) { - m_rBuilder.m_rEdges.m_targetDependEdges.push_back({m_taskId, target, true}); + rContainer.push_back({ + .task = m_taskId, + .pipeline = spec.m_pipeline, + .stage = spec.m_stage + }); } return static_cast(*this); } - TASKREF_T& trigger_on(std::initializer_list targets) noexcept + TASKREF_T& run_on(ArrayView const specs) noexcept { - return trigger_on(Corrade::Containers::arrayView(targets)); + return add_edges(m_rBuilder.m_rEdges.m_runOn, specs); } - TASKREF_T& depends_on(ArrayView const targets) noexcept + TASKREF_T& run_on(std::initializer_list specs) noexcept { - for (TargetId const target : targets) - { - m_rBuilder.m_rEdges.m_targetDependEdges.push_back({m_taskId, target, false}); - } - return static_cast(*this); + return add_edges(m_rBuilder.m_rEdges.m_runOn, specs); } - TASKREF_T& depends_on(std::initializer_list targets) noexcept + TASKREF_T& sync_with(ArrayView const specs) noexcept { - return depends_on(Corrade::Containers::arrayView(targets)); + return add_edges(m_rBuilder.m_rEdges.m_syncWith, specs); } - TASKREF_T& fulfills(ArrayView const targets) noexcept + TASKREF_T& sync_with(std::initializer_list specs) noexcept { - for (TargetId const target : targets) - { - m_rBuilder.m_rEdges.m_targetFulfillEdges.push_back({m_taskId, target, false}); - } - return static_cast(*this); + return add_edges(m_rBuilder.m_rEdges.m_syncWith, specs); + } + + TASKREF_T& triggers(ArrayView const specs) noexcept + { + return add_edges(m_rBuilder.m_rEdges.m_triggers, specs); } - TASKREF_T& fulfills(std::initializer_list targets) noexcept + TASKREF_T& triggers(std::initializer_list specs) noexcept { - return fulfills(Corrade::Containers::arrayView(targets)); + return add_edges(m_rBuilder.m_rEdges.m_triggers, specs); } TaskId m_taskId; diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index eeee509a..8e4bb7a5 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -25,131 +25,264 @@ #include "execute.h" - namespace osp { -static bool try_enqueue_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task, TargetId const calledFrom) noexcept +static bool eval_stage_enter_req(ExecContext &rExec, std::vector const& reqirements) { - if (rExec.m_tasksQueued.contains(task)) + bool out = true; + + for (auto const [reqTask, reqPipeline, reqStage] : reqirements) { - return false; // task already queued - } + ExecPipeline &rReqExecPl = rExec.m_plData[reqPipeline]; + bool const pipelineDirty = rExec.m_plDirty.test(std::size_t(reqPipeline)); + bool const stageMatches = rReqExecPl.m_currentStage == reqStage; + bool const taskRunning = rExec.m_tasksQueuedRun .contains(reqTask); + bool const taskBlocked = rExec.m_tasksQueuedBlocked.contains(reqTask); + bool const allTasksDone = (rReqExecPl.m_tasksQueuedRun == 0) + && (rReqExecPl.m_tasksQueuedBlocked == 0); - bool const prevTriggered = rExec.m_taskTriggered.test(std::size_t(task)); - bool justTriggered = false; - bool dontEnqueueYet = false; + // All required Pipelines must be on the correct stage to allow incrementing stage + out &= stageMatches; - for (DependOn const dependOn : graph.m_taskDependOn[std::size_t(task)]) - { - // If true: depend-on target is pending, wait for other tasks to finish - dontEnqueueYet |= rExec.m_targetPendingCount[dependOn.m_target] != 0; + // All required Tasks must not be in-progress (completed) to allow incrementing stage + out &= ! (taskRunning || taskBlocked); - // If true: depend-on target is fulfilled by a task that will be enqueued, wait for it - dontEnqueueYet |= rExec.m_targetWillBePending.test(std::size_t(dependOn.m_target)); + if (taskBlocked) + { + rExec.m_tasksTryRun.set(std::size_t(reqTask)); + } - if ( ! prevTriggered && dependOn.m_target == calledFrom) + if ( ( ! stageMatches ) && ( ! pipelineDirty ) && allTasksDone ) { - justTriggered = dependOn.m_trigger; + // Pipeline is not running tasks and not on the right stage. We need it to advance even + // though it has no tasks to run. For this, mark it as 'required' to be set dirty later + rExec.m_plRequired.set(std::size_t(reqPipeline)); } } - // 'Triggered' indicates that one of the (trigger=true) depend-on targets became dirty for the - // period of time since this task last ran. - if (justTriggered) - { - rExec.m_taskTriggered.set(std::size_t(task)); - } + return out; +} - if (dontEnqueueYet) - { - return false; - } +static void advance_pipeline(ExecContext &rExec, TaskGraphPipeline const &graphPl, ExecPipeline &rExecPl, PipelineId const pipeline, StageId& rNextStage) +{ + auto const stageCount = int(graphPl.m_stages.size()); + + rNextStage = rExecPl.m_currentStage; - // At least one of the trigger depend-on targets must have been dirty previously to enqueue - if ( ! (justTriggered || prevTriggered) ) + while(true) { - return false; + auto const nextStageInt = int(rNextStage); + + bool const triggered = rExecPl.m_triggered .test(nextStageInt); + bool const waited = rExecPl.m_stageCounts[nextStageInt].m_waitingTasks != 0; + + StageId const nextNextStage = stage_next(rNextStage, stageCount); + bool const canIncrement = eval_stage_enter_req(rExec, graphPl.m_stages[nextNextStage].m_enterReq); + + if ( ( ! canIncrement ) || triggered || waited ) + { + return; + } + + rNextStage = nextNextStage; + + // No triggered and no waiting tasks means infinite loop. Stage should still move by 1 though. + if ( ( ! rExecPl.m_triggered.any() ) + && ( rExecPl.m_waitingTasksTotal == 0 ) ) + { + return; + } } +} + +static void update_pipeline(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +{ + ExecPipeline &rPlExec = rExec.m_plData[pipeline]; + StageId const stage = rExec.m_plNextStage[pipeline]; + StageBits_t const stageBit = 1 << int(stage); - // Enqueue task + rPlExec.m_currentStage = stage; - for (DependOn const dependOn : graph.m_taskDependOn[std::size_t(task)]) + if ( (rPlExec.m_triggered & stageBit) == 0 ) { - ++ rExec.m_targetInUseCount[dependOn.m_target]; + return; // Not triggered } - for (TargetId const fulfill : graph.m_taskFulfill[std::size_t(task)]) + rPlExec.m_triggered ^= stageBit; + + // Enqueue all tasks + + auto const& runTasks = graph.m_pipelines[pipeline].m_stages[stage].m_runTasks; + + for (TaskId task : runTasks) { - ++ rExec.m_targetPendingCount[fulfill]; - } + std::cout << "Enqueue: " << int(task) << " "; - rExec.m_tasksQueued.emplace(task); + bool blocked = false; + for (auto const [waitPipeline, waitStage] : graph.m_taskSync[TaskInt(task)]) + { + ExecPipeline &rWaitPlExec = rExec.m_plData[waitPipeline]; + ++ rWaitPlExec.m_stageCounts[std::size_t(waitStage)].m_waitingTasks; + ++ rWaitPlExec.m_waitingTasksTotal; + + if (rWaitPlExec.m_currentStage != waitStage) + { + blocked = true; + } + } - return true; + if (blocked) + { + ++rPlExec.m_tasksQueuedBlocked; + rExec.m_tasksQueuedBlocked.emplace(task); + } + else + { + ++rPlExec.m_tasksQueuedRun; + rExec.m_tasksQueuedRun.emplace(task); + } + } } -int enqueue_dirty(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec) noexcept + +void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept { - // Find out which targets will be pending - for (std::size_t const target : rExec.m_targetDirty.ones()) + auto const task_can_run = [&graph, &rExec] (TaskId const task) { - rExec.m_targetDirty.set(target); - - for (TaskId const dependent : graph.m_targetDependents[target]) + for (auto const [waitPipeline, waitStage] : graph.m_taskSync[TaskInt(task)]) { - for (TargetId const fulfill : graph.m_taskFulfill[std::size_t(dependent)]) + if (rExec.m_plData[waitPipeline].m_currentStage != waitStage) { - rExec.m_targetWillBePending.set(std::size_t(fulfill)); + return false; } } - } - int tasksEnqueued = 0; + return true; + }; + + auto const plDirtyOnes = rExec.m_plDirty.ones(); - for (std::size_t const target : rExec.m_targetDirty.ones()) + while (plDirtyOnes.begin() != plDirtyOnes.end()) { - for (TaskId const dependent : graph.m_targetDependents[target]) + // 1. Advance pipelines and determine next stages. Writes to... + // * rExec.m_plNextStage - Next stages for each dirty pipeline + // * rExec.m_plRequired - Pipelines that need to be dirty next + // * rExec.m_tasksTryRun - Blocked tasks that might be able to start + for (std::size_t const pipelineInt : plDirtyOnes) { - tasksEnqueued += int(try_enqueue_task(tasks, graph, rExec, dependent, TargetId(target))); + std::cout << "pipeline dirty: " << pipelineInt << "\n"; + auto const pipeline = PipelineId(pipelineInt); + ExecPipeline &rPlExec = rExec.m_plData[pipeline]; + + if ( rPlExec.m_tasksQueuedBlocked == 0 && rPlExec.m_tasksQueuedRun == 0 ) + { + advance_pipeline(rExec, + graph.m_pipelines[pipeline], + rPlExec, + pipeline, + rExec.m_plNextStage[pipeline]); + } + } + + // 2. Apply new stages and enqueue tasks. Writes to... + // * rExec.m_plData[n] - Pipeline trigger flags and counts for queued tasks + // * rExec.m_tasksQueuedBlocked - Queued task that are blocked + // * rExec.m_tasksQueuedRun - Queued task that can run right away + for (std::size_t const pipelineInt : plDirtyOnes) + { + update_pipeline(tasks, graph, rExec, PipelineId(pipelineInt)); + } + + // 3. Try running some blocked tasks that might be able to run after pipelines advanced + for (std::size_t const taskInt : rExec.m_tasksTryRun.ones()) + { + auto const task = TaskId(taskInt); + LGRN_ASSERT(rExec.m_tasksQueuedBlocked.contains(task)); + + if (task_can_run(task)) + { + std::cout << "Unblock " << int(task) << "\n"; + + rExec.m_tasksQueuedBlocked.remove(task); + rExec.m_tasksQueuedRun.emplace(task); + + for (auto const [pipeline, stage] : graph.m_taskRunOn[TaskInt(task)]) + { + ExecPipeline &plExec = rExec.m_plData[pipeline]; + + --plExec.m_tasksQueuedBlocked; + ++plExec.m_tasksQueuedRun; + } + } } + rExec.m_tasksTryRun.reset(); + + // 3. 'Required' pipeline is set as dirty for next iteration. + std::copy(rExec.m_plRequired.ints().begin(), + rExec.m_plRequired.ints().end(), + rExec.m_plDirty .ints().begin()); + rExec.m_plRequired.reset(); } - rExec.m_targetWillBePending.reset(); - rExec.m_targetDirty.reset(); - return tasksEnqueued; + rExec.m_plDirty.reset(); } -void mark_completed_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept +void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept { - LGRN_ASSERTMV(rExec.m_tasksQueued.contains(task), - "Task must be queued to have been allowed to run", std::size_t(task)); + LGRN_ASSERT(rExec.m_tasksQueuedRun.contains(task)); + rExec.m_tasksQueuedRun.erase(task); - rExec.m_taskTriggered.reset(std::size_t(task)); + bool allPipelinesComplete = true; - for (DependOn const dependOn : graph.m_taskDependOn[std::size_t(task)]) + for (auto const [pipeline, stage] : graph.m_taskRunOn[TaskInt(task)]) { - -- rExec.m_targetInUseCount[dependOn.m_target]; + ExecPipeline &plExec = rExec.m_plData[pipeline]; + + plExec.m_tasksQueuedRun --; + if (plExec.m_tasksQueuedRun == 0) + { + rExec.m_plDirty.set(std::size_t(pipeline)); + } + else + { + allPipelinesComplete = false; + } } - auto const taskFulfill = lgrn::Span(graph.m_taskFulfill[std::size_t(task)]); + for (auto const [waitPipeline, waitStage] : graph.m_taskSync[TaskInt(task)]) + { + ExecPipeline &rWaitPlExec = rExec.m_plData[waitPipeline]; + auto &rStgWaitingTasks = rWaitPlExec.m_stageCounts[std::size_t(waitStage)].m_waitingTasks; + -- rStgWaitingTasks; + -- rWaitPlExec.m_waitingTasksTotal; - for (int i = 0; i < taskFulfill.size(); ++i) + if (rStgWaitingTasks == 0 && rWaitPlExec.m_tasksQueuedRun == 0 && rWaitPlExec.m_tasksQueuedBlocked == 0) + { + rExec.m_plDirty.set(std::size_t(waitPipeline)); + } + } + + if ( ! allPipelinesComplete ) { - TargetId const fulfill = taskFulfill[i]; + return; + } - -- rExec.m_targetPendingCount[fulfill]; + auto const triggers = lgrn::Span(graph.m_taskTriggers[TaskInt(task)]); - if (rExec.m_targetPendingCount[fulfill] == 0) + for (int i = 0; i < triggers.size(); ++i) + { + if (dirty.test(i)) { - if (dirty.test(i)) - { - rExec.m_targetDirty.set(std::size_t(fulfill)); - } + auto const [pipeline, stage] = triggers[i]; + + rExec.m_plData[pipeline].m_triggered |= 1 << int(stage); + rExec.m_plDirty.set(std::size_t(pipeline)); } } - rExec.m_tasksQueued.erase(task); } } // namespace osp + + diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 47de2888..ddb859f6 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -39,29 +39,65 @@ namespace osp { +struct StageCounts +{ + uint16_t m_waitingTasks {0}; +}; + +struct ExecPipeline +{ + StageBits_t m_triggered {0}; + int m_tasksQueuedRun {0}; + int m_tasksQueuedBlocked{0}; + StageId m_currentStage {0}; + uint32_t m_waitingTasksTotal {0}; + + std::array m_stageCounts; +}; + +struct TaskWaiting +{ + TaskId m_recipient; + PipelineId m_waitFor; + StageId m_waitForStage; +}; + +/** + * @brief The ExecContext class + * + * Pipelines are marked dirty (m_plDirty.set(pipeline int)) if... + * * All of its running tasks just finished + * * Not running but required by another pipeline + */ struct ExecContext { void resize(Tasks const& tasks) { - std::size_t const maxTargets = tasks.m_targetIds.capacity(); std::size_t const maxTasks = tasks.m_taskIds.capacity(); + std::size_t const maxPipeline = tasks.m_pipelineIds.capacity(); - m_tasksQueued.reserve(maxTasks); - bitvector_resize(m_taskTriggered, maxTasks); + m_tasksQueuedRun .reserve(maxTasks); + m_tasksQueuedBlocked.reserve(maxTasks); + bitvector_resize(m_tasksTryRun, maxTasks); - bitvector_resize(m_targetDirty, maxTargets); - bitvector_resize(m_targetWillBePending, maxTargets); - m_targetPendingCount.resize(maxTargets); - m_targetInUseCount .resize(maxTargets); + m_plData.resize(maxPipeline); + bitvector_resize(m_plDirty, maxPipeline); + + m_plNextStage.resize(maxPipeline); + bitvector_resize(m_plRequired, maxPipeline); } - entt::basic_sparse_set m_tasksQueued; - BitVector_t m_taskTriggered; + KeyedVec m_plData; + BitVector_t m_plDirty; + + entt::basic_sparse_set m_tasksQueuedRun; + entt::basic_sparse_set m_tasksQueuedBlocked; + BitVector_t m_tasksTryRun; - BitVector_t m_targetDirty; - BitVector_t m_targetWillBePending; - KeyedVec m_targetPendingCount; - KeyedVec m_targetInUseCount; + // used for updating + + BitVector_t m_plRequired; + KeyedVec m_plNextStage; // TODO: Consider multithreading. something something work stealing... // * Allow multiple threads to search for and execute tasks. Atomic access @@ -74,9 +110,22 @@ struct ExecContext }; // struct ExecContext -int enqueue_dirty(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec) noexcept; +//inline bool compare_syncs(SyncWaiting const& lhs, SyncWaiting const& rhs) +//{ +// return (lhs.m_recipient < rhs.m_recipient) +// && (lhs.m_recipient < rhs.m_waitFor); +//}; + +template +inline void set_dirty(ExecContext &rExec, PipelineDef const pipeline, STAGE_ENUM_T const stage) +{ + rExec.m_plData[pipeline].m_triggered |= 1 << int(stage); + rExec.m_plDirty.set(std::size_t(pipeline)); +} + +void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; -void mark_completed_task(Tasks const& tasks, ExecGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept; +void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept; diff --git a/src/osp/tasks/sequence.h b/src/osp/tasks/sequence.h new file mode 100644 index 00000000..e69de29b diff --git a/src/osp/tasks/task_types.h b/src/osp/tasks/task_types.h new file mode 100644 index 00000000..af733193 --- /dev/null +++ b/src/osp/tasks/task_types.h @@ -0,0 +1,34 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * 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 osp +{ + + + +} // namespace osp diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index 8906e910..afe9cb9c 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -29,125 +29,159 @@ namespace osp struct TaskCounts { - unsigned int m_dependingOn {0}; - unsigned int m_fulfills {0}; - unsigned int m_acquires {0}; + std::size_t runOn {0}; + std::size_t syncs {0}; + std::size_t triggers {0}; +}; + +struct PipelineCounts +{ + std::size_t runTasks {0}; + std::size_t syncedBy {0}; + std::size_t triggeredBy {0}; + uint8_t stages {0}; }; struct TargetCounts { - unsigned int m_dependents {0}; - unsigned int m_fulfilledBy {0}; + }; -ExecGraph make_exec_graph(Tasks const& tasks, ArrayView const data) +TaskGraph make_exec_graph(Tasks const& tasks, ArrayView const data) { - using TargetInt = Tasks::TargetInt; - using TaskInt = Tasks::TaskInt; + TaskGraph out; - ExecGraph out; + std::size_t const maxPipelines = tasks.m_pipelineIds.capacity(); + std::size_t const maxTasks = tasks.m_taskIds.capacity(); - KeyedVec taskCounts; - KeyedVec targetCounts; + KeyedVec plCounts; + KeyedVec taskCounts; - taskCounts .resize(tasks.m_taskIds .capacity()); - targetCounts.resize(tasks.m_targetIds.capacity()); + out.m_pipelines .resize(maxPipelines); + plCounts .resize(maxPipelines); + taskCounts .resize(maxTasks); - // Count connections + std::size_t runTotal = 0; + std::size_t syncTotal = 0; + std::size_t triggerTotal = 0; - std::size_t totalDependEdges = 0; - std::size_t totalFulfillEdges = 0; + // Count for (TaskEdges const* pEdges : data) { - totalDependEdges += pEdges->m_targetDependEdges.size(); - totalFulfillEdges += pEdges->m_targetFulfillEdges.size(); + runTotal += pEdges->m_runOn .size(); + syncTotal += pEdges->m_syncWith.size(); + triggerTotal += pEdges->m_triggers.size(); + + auto const count_stage = [&plCounts] (PipelineId const pipeline, StageId const stage) + { + uint8_t &rStageCount = plCounts[pipeline].stages; + rStageCount = std::max(rStageCount, uint8_t(uint8_t(stage) + 1)); + }; - for (auto const [task, target, _] : pEdges->m_targetDependEdges) + for (auto const [task, pipeline, stage] : pEdges->m_runOn) { - ++ taskCounts[task] .m_dependingOn; - ++ targetCounts[target].m_dependents; + plCounts[pipeline] .runTasks ++; + taskCounts[task] .runOn ++; + count_stage(pipeline, stage); } - for (auto const [task, target, _] : pEdges->m_targetFulfillEdges) + for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { - ++ taskCounts[task] .m_fulfills; - ++ targetCounts[target].m_fulfilledBy; + plCounts[pipeline] .syncedBy ++; + taskCounts[task] .syncs ++; + count_stage(pipeline, stage); + } + + for (auto const [task, pipeline, stage] : pEdges->m_triggers) + { + plCounts[pipeline] .triggeredBy ++; + taskCounts[task] .triggers ++; + count_stage(pipeline, stage); } } - // Allocate + // Allocate / reserve - out.m_taskDependOn .ids_reserve (tasks.m_taskIds.capacity()); - out.m_taskDependOn .data_reserve (totalDependEdges); - out.m_targetDependents .ids_reserve (tasks.m_targetIds.capacity()); - out.m_targetDependents .data_reserve (totalDependEdges); - out.m_taskFulfill .ids_reserve (tasks.m_taskIds.capacity()); - out.m_taskFulfill .data_reserve (totalFulfillEdges); - out.m_targetFulfilledBy .ids_reserve (tasks.m_targetIds.capacity()); - out.m_targetFulfilledBy .data_reserve (totalFulfillEdges); + out.m_taskRunOn .ids_reserve(maxTasks); + out.m_taskSync .ids_reserve(maxTasks); + out.m_taskTriggers .ids_reserve(maxTasks); + out.m_taskRunOn .data_reserve(runTotal); + out.m_taskSync .data_reserve(syncTotal); + out.m_taskTriggers .data_reserve(triggerTotal); - // Reserve partitions + for (std::size_t const pipelineInt : tasks.m_pipelineIds.bitview().zeros()) + { + auto const pipeline = PipelineId(pipelineInt); + out.m_pipelines[pipeline].m_stages.resize(plCounts[pipeline].stages); + } - for (Tasks::TaskInt const task : tasks.m_taskIds.bitview().zeros()) + for (TaskInt const taskInt : tasks.m_taskIds.bitview().zeros()) { - if (std::size_t const size = taskCounts[TaskId(task)].m_dependingOn; - size != 0) - { - out.m_taskDependOn.emplace(task, size); - } - if (std::size_t const size = taskCounts[TaskId(task)].m_fulfills; - size != 0) - { - out.m_taskFulfill.emplace(task, size); - } + TaskCounts &rCounts = taskCounts[TaskId(taskInt)]; + if (rCounts.runOn != 0) { out.m_taskRunOn .emplace(taskInt, rCounts.runOn); } + if (rCounts.syncs != 0) { out.m_taskSync .emplace(taskInt, rCounts.syncs); } + if (rCounts.triggers != 0) { out.m_taskTriggers.emplace(taskInt, rCounts.triggers); } } - for (Tasks::TargetInt const target : tasks.m_targetIds.bitview().zeros()) + // Push + + for (TaskEdges const* pEdges : data) { - if (std::size_t const size = targetCounts[TargetId(target)].m_dependents; - size != 0) - { - out.m_targetDependents.emplace(target, size); - } + // taskCounts repurposed as items remaining - if (std::size_t const size = targetCounts[TargetId(target)].m_fulfilledBy; - size != 0) + for (auto const [task, pipeline, stage] : pEdges->m_runOn) { - out.m_targetFulfilledBy.emplace(target, size); + out.m_pipelines[pipeline].m_stages[stage].m_runTasks.push_back(task); + + auto const span = lgrn::Span(out.m_taskRunOn[TaskInt(task)]); + TaskCounts &rCounts = taskCounts[task]; + span[span.size()-rCounts.runOn] = { pipeline, stage }; + rCounts.runOn --; } } - // Place connections into allocated partitions - for (TaskEdges const* pEdges : data) { - for (auto const [task, target, trigger] : pEdges->m_targetDependEdges) + for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { - auto const dependOn = lgrn::Span (out.m_taskDependOn[TaskInt(task)]); - auto const dependents = lgrn::Span (out.m_targetDependents[TargetInt(target)]); + auto const span = lgrn::Span(out.m_taskSync[TaskInt(task)]); + TaskCounts &rCounts = taskCounts[task]; - dependOn [dependOn.size() - taskCounts[task].m_dependingOn] = {target, trigger}; - dependents [dependents.size() - targetCounts[target].m_dependents] = task; + span[span.size()-rCounts.syncs] = { pipeline, stage }; + rCounts.syncs --; - --taskCounts[task] .m_dependingOn; - --targetCounts[target].m_dependents; + for (auto const [runPipeline, runStage] : out.m_taskRunOn[TaskInt(task)]) + { + out.m_pipelines[pipeline].m_stages[stage_next(stage, plCounts[pipeline].stages)].m_enterReq.push_back({task, runPipeline, runStage}); + } } - for (auto const [task, target, _] : pEdges->m_targetFulfillEdges) + for (auto const [task, pipeline, stage] : pEdges->m_triggers) { - auto const fulfills = lgrn::Span (out.m_taskFulfill[TaskInt(task)]); - auto const fulfilledBy = lgrn::Span (out.m_targetFulfilledBy[TargetInt(target)]); + auto const span = lgrn::Span(out.m_taskTriggers[TaskInt(task)]); + TaskCounts &rCounts = taskCounts[task]; - fulfills [fulfills.size() - taskCounts[task].m_fulfills] = target; - fulfilledBy [fulfilledBy.size() - targetCounts[target].m_fulfilledBy] = task; + span[span.size()-rCounts.triggers] = { pipeline, stage }; + rCounts.triggers --; - -- taskCounts[task] .m_fulfills; - -- targetCounts[target].m_fulfilledBy; + out.m_pipelines[pipeline].m_stages[stage].m_enterReq.push_back({task, pipeline, stage}); + + for (auto const [runPipeline, runStage] : out.m_taskRunOn[TaskInt(task)]) + { + out.m_pipelines[pipeline].m_stages[stage].m_enterReq.push_back({task, runPipeline, runStage}); + } } } + [[maybe_unused]] auto const all_counts_zero = [] (TaskCounts const counts) + { + return counts.syncs == 0 && counts.triggers == 0; + }; + + LGRN_ASSERTM(std::all_of(taskCounts.begin(), taskCounts.end(), all_counts_zero), + "Counts repurposed as items remaining, and must all be zero by the end here"); return out; } diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 3c5e5b7d..8193b5bf 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -37,61 +37,79 @@ namespace osp { -enum class TaskId : uint32_t { }; -enum class TargetId : uint32_t { }; -enum class SemaphoreId : uint32_t { }; +constexpr std::size_t gc_maxStages = 16; + +using StageBits_t = std::bitset; + +using TaskInt = uint32_t; +using PipelineInt = uint32_t; +using StageInt = uint8_t; +using SemaphoreInt = uint32_t; + +enum class TaskId : TaskInt { }; +enum class PipelineId : PipelineInt { }; +enum class StageId : StageInt { }; +enum class SemaphoreId : SemaphoreInt { }; struct Tasks { - using TaskInt = uint32_t; - using TargetInt = uint32_t; - using SemaphoreInt = uint32_t; - lgrn::IdRegistryStl m_taskIds; - lgrn::IdRegistryStl m_targetIds; + lgrn::IdRegistryStl m_pipelineIds; lgrn::IdRegistryStl m_semaIds; KeyedVec m_semaLimits; }; +struct TplTaskPipelineStage +{ + TaskId task; + PipelineId pipeline; + StageId stage; +}; + +struct TplPipelineStage +{ + PipelineId pipeline; + StageId stage; +}; + +struct TplTaskSemaphore +{ + TaskId task; + SemaphoreId semaphore; +}; + struct TaskEdges { - struct TargetPair - { - TaskId m_task; - TargetId m_target; - bool m_trigger; // unused for Fulfill edges - }; - - struct SemaphorePair - { - TaskId m_task; - SemaphoreId m_sema; - }; - - std::vector m_targetDependEdges; - std::vector m_targetFulfillEdges; - std::vector m_semaphoreEdges; + std::vector m_runOn; + std::vector m_syncWith; + std::vector m_triggers; + std::vector m_semaphoreEdges; }; -struct DependOn +struct TaskGraphStage { - TargetId m_target; - bool m_trigger; + std::vector m_runTasks; + std::vector m_triggeredBy; + std::vector m_syncedBy; + std::vector m_enterReq; +}; - constexpr operator TargetId() const noexcept { return m_target; } +struct TaskGraphPipeline +{ + KeyedVec m_stages; }; -struct ExecGraph +struct TaskGraph { - lgrn::IntArrayMultiMap m_taskDependOn; /// Tasks depend on (n) Targets - lgrn::IntArrayMultiMap m_targetDependents; /// Targets have (n) Tasks that depend on it + KeyedVec m_pipelines; - lgrn::IntArrayMultiMap m_taskFulfill; /// Tasks fulfill (n) Targets - lgrn::IntArrayMultiMap m_targetFulfilledBy; /// Targets are fulfilled by (n) Tasks + lgrn::IntArrayMultiMap m_taskRunOn; + lgrn::IntArrayMultiMap m_taskTriggers; + lgrn::IntArrayMultiMap m_taskSync; - lgrn::IntArrayMultiMap m_taskAcquire; /// Tasks acquire (n) Semaphores - lgrn::IntArrayMultiMap m_semaAcquiredBy; /// Semaphores are acquired by (n) Tasks + lgrn::IntArrayMultiMap m_taskAcquire; /// Tasks acquire (n) Semaphores + lgrn::IntArrayMultiMap m_semaAcquiredBy; /// Semaphores are acquired by (n) Tasks }; /** @@ -100,11 +118,29 @@ struct ExecGraph using FulfillDirty_t = lgrn::BitView>; -ExecGraph make_exec_graph(Tasks const& tasks, ArrayView data); +TaskGraph make_exec_graph(Tasks const& tasks, ArrayView data); -inline ExecGraph make_exec_graph(Tasks const& tasks, std::initializer_list data) +inline TaskGraph make_exec_graph(Tasks const& tasks, std::initializer_list data) { return make_exec_graph(tasks, arrayView(data)); } +template +struct PipelineDef +{ + operator PipelineId() const noexcept { return m_value; } + operator std::size_t() const noexcept { return std::size_t(m_value); } + + PipelineId& operator=(PipelineId const assign) { m_value = assign; return m_value; } + + PipelineId m_value; +}; + +constexpr StageId stage_next(StageId const in, int stageCount) noexcept +{ + int const next = int(in) + 1; + return StageId( (next == stageCount) ? 0 : next ); +} + + } // namespace osp diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index 8de9f8c5..fc38ddf7 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -39,7 +39,7 @@ namespace osp { -void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, WorkerContext worker) +void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, WorkerContext worker) { std::vector topDataRefs; @@ -94,7 +94,7 @@ void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec } } -void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, ArrayView enqueue) +void top_enqueue_quick(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ArrayView enqueue) { for (TargetId const target : enqueue) { @@ -106,7 +106,7 @@ void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecContext& -void write_dot_graph(std::ostream& rStream, Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData) +void write_dot_graph(std::ostream& rStream, Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData) { rStream << "graph {\n" << "rankdir=LR;\n" diff --git a/src/osp/tasks/top_execute.h b/src/osp/tasks/top_execute.h index 7b22a0cb..5c50c729 100644 --- a/src/osp/tasks/top_execute.h +++ b/src/osp/tasks/top_execute.h @@ -33,15 +33,15 @@ namespace osp { -void top_run_blocking(Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, WorkerContext worker = {}); +void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, WorkerContext worker = {}); -void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, ArrayView enqueue); +void top_enqueue_quick(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ArrayView enqueue); -inline void top_enqueue_quick(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, std::initializer_list enqueue) +inline void top_enqueue_quick(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, std::initializer_list enqueue) { return top_enqueue_quick(tasks, graph, rExec, Corrade::Containers::arrayView(enqueue)); } -void write_dot_graph(std::ostream& rStream, Tasks const& tasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData); +void write_dot_graph(std::ostream& rStream, Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData); } // namespace testapp diff --git a/src/osp/tasks/top_session.cpp b/src/osp/tasks/top_session.cpp index 7010b4b3..de24d278 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/osp/tasks/top_session.cpp @@ -38,7 +38,7 @@ namespace osp void top_close_session( Tasks & rTasks, - ExecGraph const& graph, + TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index d0c48c67..f88e78d1 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -135,7 +135,7 @@ struct SessionGroup /** * @brief Close sessions, delete all their associated TopData, Tasks, and Targets. */ -void top_close_session(Tasks& rTasks, ExecGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, ArrayView sessions); +void top_close_session(Tasks& rTasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, ArrayView sessions); } // namespace osp diff --git a/src/test_application/testapp.h b/src/test_application/testapp.h index 5dbc2a77..056c4911 100644 --- a/src/test_application/testapp.h +++ b/src/test_application/testapp.h @@ -50,7 +50,7 @@ struct TestAppTasks osp::Tasks m_tasks; osp::TopTaskDataVec_t m_taskData; osp::ExecContext m_exec; - std::optional m_graph; + std::optional m_graph; }; struct TestApp : TestAppTasks diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 02324c64..271f0f6b 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -49,18 +49,18 @@ bool contains(RANGE_T const& range, VALUE_T const& value) noexcept } template -void randomized_singlethreaded_execute(Tasks const& tasks, ExecGraph const& graph, ExecContext& rExec, std::mt19937 &rRand, int maxRuns, RUN_TASK_T && runTask) +void randomized_singlethreaded_execute(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, std::mt19937 &rRand, int maxRuns, RUN_TASK_T && runTask) { for (int i = 0; i < maxRuns; ++i) { - std::size_t const tasksLeft = rExec.m_tasksQueued.size(); + std::size_t const tasksLeft = rExec.m_tasksQueuedRun.size(); if (tasksLeft == 0) { break; } - TaskId const randomTask = rExec.m_tasksQueued.at(rRand() % tasksLeft); + TaskId const randomTask = rExec.m_tasksQueuedRun.at(rRand() % tasksLeft); FulfillDirty_t const status = runTask(randomTask); mark_completed_task(tasks, graph, rExec, randomTask, status); @@ -70,9 +70,23 @@ void randomized_singlethreaded_execute(Tasks const& tasks, ExecGraph const& grap //----------------------------------------------------------------------------- -// Test multiple tasks fulfilling a single target +namespace test_a +{ + +enum class Stages { Clear, Fill, Use }; + +struct Pipelines +{ + osp::PipelineDef vec; +}; + +} // namespace test_a + +// Test pipeline consisting of parallel tasks TEST(Tasks, BasicParallelSingleThreaded) { + using namespace test_a; + using enum Stages; // NOTE // If this was multithreaded, then multiple threads writing to a single container is a bad // idea. The proper way to do this is to make a vector per-thread. Targets are still @@ -84,77 +98,84 @@ TEST(Tasks, BasicParallelSingleThreaded) using TaskFuncVec_t = BasicTraits_t::FuncVec_t; constexpr int sc_repetitions = 32; - constexpr int sc_pusherTaskCount = 1337; + constexpr int sc_pusherTaskCount = 24; constexpr int sc_totalTaskCount = sc_pusherTaskCount + 1; std::mt19937 randGen(69); + // Step 1: Create tasks + Tasks tasks; TaskEdges edges; TaskFuncVec_t functions; Builder_t builder{tasks, edges, functions}; - auto const - [ - inputIn, /// Input int, manually set dirty when it changes - vecClear, /// Vector must be cleared before new stuff can be added to it - vecReady /// Vector is ready, all ints have been added to it - - ] = builder.create_targets<3>(); - - // Clear vector before use - builder.task() - .trigger_on({inputIn}) - .fulfills({vecClear}) - .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t - { - rOut.clear(); - return {{0b01}}; - }); + auto pl = builder.create_pipelines(); // Multiple tasks push to the vector for (int i = 0; i < sc_pusherTaskCount; ++i) { builder.task() - .trigger_on({vecClear}) - .fulfills({vecReady}) - .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t + .run_on ({{pl.vec, Fill}}) + .triggers({{pl.vec, Clear}}) + .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t { rOut.push_back(in); return {{0b01}}; }); } - ExecGraph const graph = make_exec_graph(tasks, {&edges}); - ExecContext exec; + // Use vector + builder.task() + .run_on({{pl.vec, Use}}) + .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t + { + int const sum = std::accumulate(rOut.begin(), rOut.end(), 0); + EXPECT_EQ(sum, in * sc_pusherTaskCount); + return {{0b0}}; + }); + + // Clear vector after use + builder.task() + .run_on({{pl.vec, Clear}}) + .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t + { + rOut.clear(); + return {{0b01}}; + }); + + // Step 2: Compile tasks into an execution graph + TaskGraph const graph = make_exec_graph(tasks, {&edges}); + + // Step 3: Run + + ExecContext exec; exec.resize(tasks); int input = 0; std::vector output; - // Repeat (with randomness) to test many possible execution orders + // Repeat with randomness to test many possible execution orders for (int i = 0; i < sc_repetitions; ++i) { input = 1 + randGen() % 30; - // Enqueue initial tasks - // This roughly indicates "Time has changed" and "Render requested" - exec.m_targetDirty.set(std::size_t(inputIn)); + set_dirty(exec, pl.vec, Fill); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output] (TaskId const task) -> FulfillDirty_t { return functions[task](input, output); }); - - int const sum = std::accumulate(output.begin(), output.end(), 0); - - ASSERT_EQ(sum, input * sc_pusherTaskCount); } } + //----------------------------------------------------------------------------- -struct TestWorld +namespace test_gameworld +{ + +struct World { int m_deltaTimeIn{1}; int m_forces{0}; @@ -162,23 +183,31 @@ struct TestWorld std::set m_canvas; }; -struct TestWorldTargets +enum class StgSimple { Recalc, Use }; +enum class StgRender { Render, Done }; + +struct Pipelines { - TargetId timeIn; /// External time input, manually set dirty when time 'changes', and the world needs to update - TargetId forces; /// Forces need to be calculated before physics - TargetId positions; /// Positions calculated by physics task - TargetId renderRequestIn; /// External render request, manually set dirty when a new frame to render is required - TargetId renderDoneOut; /// Fired when all rendering is finished + osp::PipelineDef time; /// External time input, manually set dirty when time 'changes', and the world needs to update + osp::PipelineDef forces; /// Forces need to be calculated before physics + osp::PipelineDef positions; /// Positions calculated by physics task + osp::PipelineDef render; /// External render request, manually set dirty when a new frame to render is required }; -// Single-threaded test against TestWorld with order-dependent tasks +} // namespace test_gameworld + +// Single-threaded test against World with order-dependent tasks TEST(Tasks, BasicSingleThreaded) { - using BasicTraits_t = BasicBuilderTraits; + using namespace test_gameworld; + using enum StgSimple; + using enum StgRender; + + using BasicTraits_t = BasicBuilderTraits; using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; - constexpr int sc_repetitions = 32; + constexpr int sc_repetitions = 128; std::mt19937 randGen(69); Tasks tasks; @@ -186,34 +215,33 @@ TEST(Tasks, BasicSingleThreaded) TaskFuncVec_t functions; Builder_t builder{tasks, edges, functions}; - auto const tgt = builder.create_targets(); + auto const pl = builder.create_pipelines(); // Start adding tasks. The order these are added does not matter. // Two tasks calculate forces needed by the physics update - TaskId const taskA = builder.task() - .trigger_on({tgt.timeIn}) - .fulfills({tgt.forces}) - .func( [] (TestWorld& rWorld) -> FulfillDirty_t + builder.task() + .run_on ({{pl.time, Use}}) + .sync_with({{pl.forces, Recalc}}) + .func( [] (World& rWorld) -> FulfillDirty_t { rWorld.m_forces += 42 * rWorld.m_deltaTimeIn; return {{0b01}}; }); - TaskId const taskB = builder.task() - .trigger_on({tgt.timeIn}) - .fulfills({tgt.forces}) - .func([] (TestWorld& rWorld) -> FulfillDirty_t + builder.task() + .run_on ({{pl.time, Use}}) + .sync_with({{pl.forces, Recalc}}) + .func([] (World& rWorld) -> FulfillDirty_t { rWorld.m_forces += 1337 * rWorld.m_deltaTimeIn; return {{0b01}}; }); // Main Physics update - TaskId const taskC = builder.task() - .trigger_on({tgt.timeIn}) - .depends_on({tgt.forces}) - .fulfills({tgt.positions}) - .func([] (TestWorld& rWorld) -> FulfillDirty_t + builder.task() + .run_on ({{pl.time, Use}}) + .sync_with({{pl.forces, Use}, {pl.positions, Recalc}}) + .func([] (World& rWorld) -> FulfillDirty_t { EXPECT_EQ(rWorld.m_forces, 1337 + 42); rWorld.m_positions += rWorld.m_forces; @@ -223,11 +251,10 @@ TEST(Tasks, BasicSingleThreaded) // Draw things moved by physics update. If 'updWorld' wasn't enqueued, then // this will still run, as no 'needPhysics' tasks are incomplete - TaskId const taskD = builder.task() - .trigger_on({tgt.renderRequestIn}) - .depends_on({tgt.positions}) - .fulfills({tgt.renderDoneOut}) - .func([] (TestWorld& rWorld) -> FulfillDirty_t + builder.task() + .run_on ({{pl.render, Render}}) + .sync_with({{pl.positions, Use}}) + .func([] (World& rWorld) -> FulfillDirty_t { EXPECT_EQ(rWorld.m_positions, 1337 + 42); rWorld.m_canvas.emplace("Physics Cube"); @@ -236,38 +263,23 @@ TEST(Tasks, BasicSingleThreaded) // Draw things unrelated to physics. This is allowed to be the first task // to run - TaskId const taskE = builder.task() - .trigger_on({tgt.renderRequestIn}) - .fulfills({tgt.renderDoneOut}) - .func([] (TestWorld& rWorld) -> FulfillDirty_t + builder.task() + .run_on ({{pl.render, Render}}) + .func([] (World& rWorld) -> FulfillDirty_t { rWorld.m_canvas.emplace("Terrain"); return {{0b01}}; }); - ExecGraph const graph = make_exec_graph(tasks, {&edges}); - - // Random checks to assure the graph structure is properly built - ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.timeIn) ].size(), 3); - ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.forces) ].size(), 1); - ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.positions) ].size(), 1); - ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.renderRequestIn) ].size(), 2); - ASSERT_EQ(graph.m_targetDependents[ uint32_t(tgt.renderDoneOut) ].size(), 0); - ASSERT_TRUE( contains(graph.m_targetFulfilledBy[uint32_t(tgt.forces)], taskB) ); - ASSERT_FALSE( contains(graph.m_targetFulfilledBy[uint32_t(tgt.renderDoneOut)], taskC) ); - ASSERT_TRUE( contains(graph.m_taskDependOn[uint32_t(taskA)], tgt.timeIn) ); - ASSERT_FALSE( contains(graph.m_taskDependOn[uint32_t(taskB)], tgt.positions) ); - ASSERT_TRUE( contains(graph.m_taskDependOn[uint32_t(taskC)], tgt.timeIn) ); - ASSERT_TRUE( contains(graph.m_taskDependOn[uint32_t(taskC)], tgt.forces) ); - ASSERT_TRUE( contains(graph.m_taskFulfill[uint32_t(taskD)], tgt.renderDoneOut) ); - ASSERT_FALSE( contains(graph.m_taskFulfill[uint32_t(taskE)], tgt.forces) ); + TaskGraph const graph = make_exec_graph(tasks, {&edges}); // Execute - TestWorld world; - ExecContext exec; + ExecContext exec; exec.resize(tasks); + World world; + // Repeat (with randomness) to test many possible execution orders for (int i = 0; i < sc_repetitions; ++i) { @@ -277,8 +289,8 @@ TEST(Tasks, BasicSingleThreaded) // Enqueue initial tasks // This roughly indicates "Time has changed" and "Render requested" - exec.m_targetDirty.set(std::size_t(tgt.timeIn)); - exec.m_targetDirty.set(std::size_t(tgt.renderRequestIn)); + set_dirty(exec, pl.time, StgSimple::Use); + set_dirty(exec, pl.render, StgRender::Render); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute( @@ -290,6 +302,8 @@ TEST(Tasks, BasicSingleThreaded) ASSERT_TRUE(world.m_canvas.contains("Physics Cube")); ASSERT_TRUE(world.m_canvas.contains("Terrain")); + + std::cout << "done!\n"; } } From 5702e3a52a0d7f25d0e817d119b7f54184fade3b Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sat, 10 Jun 2023 03:18:08 -0700 Subject: [PATCH 17/35] Rewrite pipelines (already lol) --- src/osp/tasks/execute.cpp | 390 +++++++++++++++++++------------------- src/osp/tasks/sequence.h | 0 src/osp/tasks/tasks.cpp | 336 ++++++++++++++++++++++++-------- src/osp/tasks/tasks.h | 106 +++++++++-- test/tasks/main.cpp | 142 ++++++++++++-- 5 files changed, 670 insertions(+), 304 deletions(-) delete mode 100644 src/osp/tasks/sequence.h diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 8e4bb7a5..72604146 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -64,223 +64,217 @@ static bool eval_stage_enter_req(ExecContext &rExec, std::vector(graph.m_taskTriggers[TaskInt(task)]); - - for (int i = 0; i < triggers.size(); ++i) - { - if (dirty.test(i)) - { - auto const [pipeline, stage] = triggers[i]; - - rExec.m_plData[pipeline].m_triggered |= 1 << int(stage); - rExec.m_plDirty.set(std::size_t(pipeline)); - } - } - +// LGRN_ASSERT(rExec.m_tasksQueuedRun.contains(task)); +// rExec.m_tasksQueuedRun.erase(task); + +// bool allPipelinesComplete = true; + +// for (auto const [pipeline, stage] : graph.m_taskRunOn[TaskInt(task)]) +// { +// ExecPipeline &plExec = rExec.m_plData[pipeline]; + +// plExec.m_tasksQueuedRun --; +// if (plExec.m_tasksQueuedRun == 0) +// { +// rExec.m_plDirty.set(std::size_t(pipeline)); +// } +// else +// { +// allPipelinesComplete = false; +// } +// } + +// for (auto const [waitPipeline, waitStage] : graph.m_taskSync[TaskInt(task)]) +// { +// ExecPipeline &rWaitPlExec = rExec.m_plData[waitPipeline]; +// auto &rStgWaitingTasks = rWaitPlExec.m_stageCounts[std::size_t(waitStage)].m_waitingTasks; +// -- rStgWaitingTasks; +// -- rWaitPlExec.m_waitingTasksTotal; + +// if (rStgWaitingTasks == 0 && rWaitPlExec.m_tasksQueuedRun == 0 && rWaitPlExec.m_tasksQueuedBlocked == 0) +// { +// rExec.m_plDirty.set(std::size_t(waitPipeline)); +// } +// } + +// if ( ! allPipelinesComplete ) +// { +// return; +// } + +// auto const triggers = lgrn::Span(graph.m_taskTriggers[TaskInt(task)]); + +// for (int i = 0; i < triggers.size(); ++i) +// { +// if (dirty.test(i)) +// { +// auto const [pipeline, stage] = triggers[i]; + +// rExec.m_plData[pipeline].m_triggered |= 1 << int(stage); +// rExec.m_plDirty.set(std::size_t(pipeline)); +// } +// } } } // namespace osp diff --git a/src/osp/tasks/sequence.h b/src/osp/tasks/sequence.h deleted file mode 100644 index e69de29b..00000000 diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index afe9cb9c..6b561f0f 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -24,28 +24,68 @@ */ #include "tasks.h" +#include "Corrade/Containers/ArrayViewStl.h" + +#include + namespace osp { struct TaskCounts { - std::size_t runOn {0}; - std::size_t syncs {0}; - std::size_t triggers {0}; + uint8_t runOn {0}; + uint16_t requiresStages {0}; + uint16_t requiredByStages {0}; + +}; + +struct StageCounts +{ + uint16_t runTasks {0}; + uint16_t requiresTasks {0}; + uint16_t requiredByTasks {0}; }; struct PipelineCounts { - std::size_t runTasks {0}; - std::size_t syncedBy {0}; - std::size_t triggeredBy {0}; uint8_t stages {0}; + std::array stageCounts; }; -struct TargetCounts + + +template +static void fill_many(KeyedVec& rVec, GETSIZE_T&& get_size, CLAIM_T&& claim) { + using key_int_t = lgrn::underlying_int_type_t; + using value_int_t = lgrn::underlying_int_type_t; -}; + value_int_t currentId = 0; + for (value_int_t i = 0; i < rVec.size(); ++i) + { + value_int_t const size = get_size(KEY_T(i)); + + rVec[KEY_T(i)] = VALUE_T(currentId); + + if (size != 0) + { + value_int_t const nextId = currentId + size; + + for (value_int_t j = currentId; j < nextId; ++j) + { + claim(KEY_T(i), VALUE_T(j)); + } + + currentId = nextId; + } + } +} + +template +static VALUE_T id_from_count(KeyedVec const& vec, KEY_T const key, lgrn::underlying_int_type_t const count) +{ + return VALUE_T( lgrn::underlying_int_type_t(vec[KEY_T(lgrn::underlying_int_type_t(key) + 1)]) - count ); +} TaskGraph make_exec_graph(Tasks const& tasks, ArrayView const data) @@ -55,133 +95,269 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView std::size_t const maxPipelines = tasks.m_pipelineIds.capacity(); std::size_t const maxTasks = tasks.m_taskIds.capacity(); + // counts + KeyedVec plCounts; KeyedVec taskCounts; + out.pipelineToFirstAnystg .resize(maxPipelines); + plCounts .resize(maxPipelines+1); + taskCounts .resize(maxTasks+1); - out.m_pipelines .resize(maxPipelines); - plCounts .resize(maxPipelines); - taskCounts .resize(maxTasks); + std::size_t totalTasksReqStage = 0; + std::size_t totalStageReqTasks = 0; + std::size_t totalRunTasks = 0; + std::size_t totalStages = 0; - std::size_t runTotal = 0; - std::size_t syncTotal = 0; - std::size_t triggerTotal = 0; + // Count up which pipeline/stages each task runs on - // Count + auto const count_stage = [&plCounts] (PipelineId const pipeline, StageId const stage) + { + uint8_t &rStageCount = plCounts[pipeline].stages; + rStageCount = std::max(rStageCount, uint8_t(uint8_t(stage) + 1)); + }; for (TaskEdges const* pEdges : data) { - runTotal += pEdges->m_runOn .size(); - syncTotal += pEdges->m_syncWith.size(); - triggerTotal += pEdges->m_triggers.size(); - - auto const count_stage = [&plCounts] (PipelineId const pipeline, StageId const stage) - { - uint8_t &rStageCount = plCounts[pipeline].stages; - rStageCount = std::max(rStageCount, uint8_t(uint8_t(stage) + 1)); - }; + totalRunTasks += pEdges->m_runOn.size(); for (auto const [task, pipeline, stage] : pEdges->m_runOn) { - plCounts[pipeline] .runTasks ++; - taskCounts[task] .runOn ++; + plCounts[pipeline].stageCounts[std::size_t(stage)].runTasks ++; + taskCounts[task] .runOn ++; count_stage(pipeline, stage); } for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { - plCounts[pipeline] .syncedBy ++; - taskCounts[task] .syncs ++; count_stage(pipeline, stage); } for (auto const [task, pipeline, stage] : pEdges->m_triggers) { - plCounts[pipeline] .triggeredBy ++; - taskCounts[task] .triggers ++; count_stage(pipeline, stage); } } - // Allocate / reserve - - out.m_taskRunOn .ids_reserve(maxTasks); - out.m_taskSync .ids_reserve(maxTasks); - out.m_taskTriggers .ids_reserve(maxTasks); - out.m_taskRunOn .data_reserve(runTotal); - out.m_taskSync .data_reserve(syncTotal); - out.m_taskTriggers .data_reserve(triggerTotal); + // Count total stages - for (std::size_t const pipelineInt : tasks.m_pipelineIds.bitview().zeros()) + for (PipelineCounts const& plCount : plCounts) { - auto const pipeline = PipelineId(pipelineInt); - out.m_pipelines[pipeline].m_stages.resize(plCounts[pipeline].stages); + totalStages += plCount.stages; } - for (TaskInt const taskInt : tasks.m_taskIds.bitview().zeros()) + // Count TaskRequiresStages and StageRequiresTasks + + + auto const count_stagereqtask = [&plCounts, &taskCounts, &totalStageReqTasks] + (PipelineId const pl, StageId const stg, TaskId const task) { - TaskCounts &rCounts = taskCounts[TaskId(taskInt)]; - if (rCounts.runOn != 0) { out.m_taskRunOn .emplace(taskInt, rCounts.runOn); } - if (rCounts.syncs != 0) { out.m_taskSync .emplace(taskInt, rCounts.syncs); } - if (rCounts.triggers != 0) { out.m_taskTriggers.emplace(taskInt, rCounts.triggers); } - } + StageCounts &rStageCounts = plCounts[pl].stageCounts[std::size_t(stg)]; + TaskCounts &rTaskCounts = taskCounts[task]; - // Push + rStageCounts.requiresTasks ++; + rTaskCounts .requiredByStages ++; + totalStageReqTasks ++; + }; - for (TaskEdges const* pEdges : data) + + auto const count_taskreqstage = [&plCounts, &taskCounts, &totalTasksReqStage] + (TaskId const task, PipelineId const pl, StageId const stg) { - // taskCounts repurposed as items remaining + StageCounts &rStageCounts = plCounts[pl].stageCounts[std::size_t(stg)]; + TaskCounts &rTaskCounts = taskCounts[task]; - for (auto const [task, pipeline, stage] : pEdges->m_runOn) + rTaskCounts .requiresStages ++; + rStageCounts.requiredByTasks ++; + totalTasksReqStage ++; + }; + + for (TaskEdges const* pEdges : data) + { + // Each sync-with adds... + // * TaskRequiresStage makes task require pipeline to be on stage + // * StageRequiresTask for stage to wait for task to complete + for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { - out.m_pipelines[pipeline].m_stages[stage].m_runTasks.push_back(task); + count_stagereqtask(pipeline, stage, task); + count_taskreqstage(task, pipeline, stage); + } - auto const span = lgrn::Span(out.m_taskRunOn[TaskInt(task)]); - TaskCounts &rCounts = taskCounts[task]; - span[span.size()-rCounts.runOn] = { pipeline, stage }; - rCounts.runOn --; + // Each triggers adds... + // * StageRequiresTask on previous stage to wait for task to complete + for (auto const [task, pipeline, stage] : pEdges->m_triggers) + { + count_stagereqtask(pipeline, stage_prev(stage, plCounts[pipeline].stages), task); } } + // Allocate + + out.pipelineToFirstAnystg .resize(maxPipelines+1, lgrn::id_null()); + out.anystgToPipeline .resize(totalStages+1, lgrn::id_null()); + out.anystgToFirstRuntask .resize(totalStages+1, lgrn::id_null()); + out.runtaskToTask .resize(totalRunTasks, lgrn::id_null()); + out.anystgToFirstStgreqtask .resize(totalStages+1, lgrn::id_null()); + out.stgreqtaskData .resize(totalStageReqTasks, {}); + out.taskToFirstRevStgreqtask .resize(maxTasks+1, lgrn::id_null()); + out.revStgreqtaskToStage .resize(totalStageReqTasks, lgrn::id_null()); + out.taskToFirstTaskreqstg .resize(maxTasks+1, lgrn::id_null()); + out.taskreqstgData .resize(totalTasksReqStage, {}); + out.stageToFirstRevTaskreqstg .resize(totalStages, lgrn::id_null()); + out.revTaskreqstgToTask .resize(totalTasksReqStage, lgrn::id_null()); + + // Put spaces + + fill_many( + out.pipelineToFirstAnystg, + [&plCounts] (PipelineId pl) { return plCounts[pl].stages; }, + [&out] (PipelineId pl, AnyStageId claimed) { out.anystgToPipeline[claimed] = pl; }); + + fill_many( + out.anystgToFirstRuntask, + [&plCounts, &out] (AnyStageId stg) + { + PipelineId const pl = out.anystgToPipeline[stg]; + if (pl == lgrn::id_null()) + { + return uint16_t(0); + } + StageId const stgLocal = stage_from(out, pl, stg); + return plCounts[pl].stageCounts[std::size_t(stgLocal)].runTasks; + }, + [] (AnyStageId, RunTaskId) { }); + + // for StageReqTaskId + fill_many( + out.anystgToFirstStgreqtask, + [&plCounts, &out] (AnyStageId stg) + { + PipelineId const pl = out.anystgToPipeline[stg]; + if (pl == lgrn::id_null()) + { + return uint16_t(0); + } + StageId const stgLocal = stage_from(out, pl, stg); + return plCounts[pl].stageCounts[std::size_t(stgLocal)].requiresTasks; + }, + [&out] (AnyStageId stg, StageReqTaskId claimed) { out.stgreqtaskData[claimed].ownStage = stg; }); + fill_many( + out.taskToFirstRevStgreqtask, + [&taskCounts] (TaskId task) { return taskCounts[task].requiredByStages; }, + [&out] (TaskId, ReverseStageReqTaskId) { }); + + // for TaskReqStage + fill_many( + out.taskToFirstTaskreqstg, + [&taskCounts] (TaskId task) { return taskCounts[task].requiresStages; }, + [&out] (TaskId task, TaskReqStageId claimed) { out.taskreqstgData[claimed].ownTask = task; }); + fill_many( + out.stageToFirstRevTaskreqstg, + [&plCounts, &out] (AnyStageId stg) + { + PipelineId const pl = out.anystgToPipeline[stg]; + StageId const stgLocal = stage_from(out, pl, stg); + return plCounts[pl].stageCounts[std::size_t(stgLocal)].requiredByTasks; + }, + [&out] (AnyStageId, ReverseTaskReqStageId) { }); + + // Push + + auto add_stagereqtask = [&plCounts, &taskCounts, &totalStageReqTasks, &out] + (PipelineId const pl, StageId const stg, TaskId const task) + { + AnyStageId const anystg = anystg_from(out, pl, stg); + StageCounts &rStageCounts = plCounts[pl].stageCounts[std::size_t(stg)]; + TaskCounts &rTaskCounts = taskCounts[task]; + + StageReqTaskId const stgReqTaskId = id_from_count(out.anystgToFirstStgreqtask, anystg, rStageCounts.requiresTasks); + ReverseStageReqTaskId const revStgReqTaskId = id_from_count(out.taskToFirstRevStgreqtask, task, rTaskCounts.requiredByStages); + + StageRequiresTask &rStgReqTask = out.stgreqtaskData[stgReqTaskId]; + rStgReqTask.reqTask = task; + rStgReqTask.reqPipeline = pl; + rStgReqTask.reqStage = stg; + out.revStgreqtaskToStage[revStgReqTaskId] = anystg; + + -- rStageCounts.requiresTasks; + -- rTaskCounts.requiredByStages; + -- totalStageReqTasks; + }; + + auto add_taskreqstage = [&plCounts, &taskCounts, &totalTasksReqStage, &out] (TaskId const task, PipelineId const pl, StageId const stg) + { + AnyStageId const anystg = anystg_from(out, pl, stg); + StageCounts &rStageCounts = plCounts[pl].stageCounts[std::size_t(stg)]; + TaskCounts &rTaskCounts = taskCounts[task]; + + TaskReqStageId const taskReqStgId = id_from_count(out.taskToFirstTaskreqstg, task, rTaskCounts.requiresStages); + ReverseTaskReqStageId const revTaskReqStgId = id_from_count(out.stageToFirstRevTaskreqstg, anystg, rStageCounts.requiredByTasks); + + TaskRequiresStage &rTaskReqStage = out.taskreqstgData[taskReqStgId]; + rTaskReqStage.reqStage = stg; + rTaskReqStage.reqPipeline = pl; + out.revTaskreqstgToTask[revTaskReqStgId] = task; + + -- rTaskCounts.requiresStages; + -- rStageCounts.requiredByTasks; + -- totalTasksReqStage; + }; + for (TaskEdges const* pEdges : data) { - for (auto const [task, pipeline, stage] : pEdges->m_syncWith) + for (auto const [task, pipeline, stage] : pEdges->m_runOn) { - auto const span = lgrn::Span(out.m_taskSync[TaskInt(task)]); - TaskCounts &rCounts = taskCounts[task]; + AnyStageId const anystg = anystg_from(out, pipeline, stage); + StageCounts &rStageCounts = plCounts[pipeline].stageCounts[std::size_t(stage)]; + TaskCounts &rTaskCounts = taskCounts[task]; - span[span.size()-rCounts.syncs] = { pipeline, stage }; - rCounts.syncs --; + RunTaskId const runTask = id_from_count(out.anystgToFirstRuntask, anystg, rStageCounts.runTasks); + out.runtaskToTask[runTask] = task; - for (auto const [runPipeline, runStage] : out.m_taskRunOn[TaskInt(task)]) - { - out.m_pipelines[pipeline].m_stages[stage_next(stage, plCounts[pipeline].stages)].m_enterReq.push_back({task, runPipeline, runStage}); - } + -- rStageCounts.runTasks; + -- rTaskCounts.runOn; + -- totalRunTasks; } - for (auto const [task, pipeline, stage] : pEdges->m_triggers) + for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { - auto const span = lgrn::Span(out.m_taskTriggers[TaskInt(task)]); - TaskCounts &rCounts = taskCounts[task]; - - span[span.size()-rCounts.triggers] = { pipeline, stage }; - rCounts.triggers --; + add_stagereqtask(pipeline, stage, task); + add_taskreqstage(task, pipeline, stage); + } - out.m_pipelines[pipeline].m_stages[stage].m_enterReq.push_back({task, pipeline, stage}); + for (auto const [task, pipeline, stage] : pEdges->m_triggers) + { + add_stagereqtask(pipeline, stage_prev(stage, plCounts[pipeline].stages), task); + } + } - for (auto const [runPipeline, runStage] : out.m_taskRunOn[TaskInt(task)]) + [[maybe_unused]] auto const all_counts_zero = [&plCounts, &taskCounts] () + { + for (PipelineCounts const& plCount : plCounts) + { + for (StageCounts const& stgCount : plCount.stageCounts) { - out.m_pipelines[pipeline].m_stages[stage].m_enterReq.push_back({task, runPipeline, runStage}); + if ( stgCount.requiredByTasks != 0 + || stgCount.requiresTasks != 0 + || stgCount.runTasks != 0) + { + return false; + } + } + } + for (TaskCounts const& taskCount : taskCounts) + { + if ( taskCount.requiredByStages != 0 + || taskCount.requiredByStages != 0 + || taskCount.runOn != 0) + { + return false; } } - } - [[maybe_unused]] auto const all_counts_zero = [] (TaskCounts const counts) - { - return counts.syncs == 0 && counts.triggers == 0; + return true; }; - LGRN_ASSERTM(std::all_of(taskCounts.begin(), taskCounts.end(), all_counts_zero), - "Counts repurposed as items remaining, and must all be zero by the end here"); + LGRN_ASSERTM(all_counts_zero(), "Counts repurposed as items remaining, and must all be zero by the end here"); return out; } diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 8193b5bf..1123501f 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -51,6 +51,7 @@ enum class PipelineId : PipelineInt { }; enum class StageId : StageInt { }; enum class SemaphoreId : SemaphoreInt { }; + struct Tasks { lgrn::IdRegistryStl m_taskIds; @@ -87,31 +88,87 @@ struct TaskEdges std::vector m_semaphoreEdges; }; -struct TaskGraphStage +//struct TaskGraphStage +//{ +// std::vector m_runTasks; +// std::vector m_triggeredBy; +// std::vector m_syncedBy; +// std::vector m_enterReq; +//}; + +//struct TaskGraphPipeline +//{ +// KeyedVec m_stages; +//}; + +enum class AnyStageId : uint32_t { }; +enum class RunTaskId : uint32_t { }; + +enum class StageReqTaskId : uint32_t { }; +enum class ReverseStageReqTaskId : uint32_t { }; + +enum class TaskReqStageId : uint32_t { }; +enum class ReverseTaskReqStageId : uint32_t { }; + +struct StageRequiresTask { - std::vector m_runTasks; - std::vector m_triggeredBy; - std::vector m_syncedBy; - std::vector m_enterReq; + AnyStageId ownStage { lgrn::id_null() }; + + // Task needs to be complete for requirement to be satisfied + // All requirements must be satisfied to proceed to the next stage + TaskId reqTask { lgrn::id_null() }; + PipelineId reqPipeline { lgrn::id_null() }; + StageId reqStage { lgrn::id_null() }; }; -struct TaskGraphPipeline +struct TaskRequiresStage { - KeyedVec m_stages; + TaskId ownTask { lgrn::id_null() }; + + // Pipeline must be on a certain stage for requirement to be satisfied. + // All requirements must be satisfied for the task to be unblocked + PipelineId reqPipeline { lgrn::id_null() }; + StageId reqStage { lgrn::id_null() }; }; struct TaskGraph { - KeyedVec m_pipelines; - - lgrn::IntArrayMultiMap m_taskRunOn; - lgrn::IntArrayMultiMap m_taskTriggers; - lgrn::IntArrayMultiMap m_taskSync; - - lgrn::IntArrayMultiMap m_taskAcquire; /// Tasks acquire (n) Semaphores - lgrn::IntArrayMultiMap m_semaAcquiredBy; /// Semaphores are acquired by (n) Tasks + // Each pipeline has multiple stages. + // PipelineId <--> many AnyStageIds + KeyedVec pipelineToFirstAnystg; + KeyedVec anystgToPipeline; + + // Each stage has multiple tasks to run + // AnyStageId --> TaskInStageId --> many TaskId + KeyedVec anystgToFirstRuntask; + KeyedVec runtaskToTask; + + // Each stage has multiple entrance requirements. + // AnyStageId <--> many StageEnterReqId + KeyedVec anystgToFirstStgreqtask; + KeyedVec stgreqtaskData; + // Tasks need to know which stages refer to them + // TaskId --> ReverseStageReqId --> many AnyStageId + KeyedVec taskToFirstRevStgreqtask; + KeyedVec revStgreqtaskToStage; + + + // Task requires pipelines to be on certain stages. + // TaskId <--> TaskReqId + KeyedVec taskToFirstTaskreqstg; + KeyedVec taskreqstgData; + // Stages need to know which tasks require them + // StageId --> ReverseTaskReqId --> many TaskId + KeyedVec stageToFirstRevTaskreqstg; + KeyedVec revTaskreqstgToTask; + + + // not yet used + lgrn::IntArrayMultiMap taskAcquire; /// Tasks acquire (n) Semaphores + lgrn::IntArrayMultiMap semaAcquiredBy; /// Semaphores are acquired by (n) Tasks }; + /** * @brief Bitset returned by tasks to determine which fulfill targets should be marked dirty */ @@ -136,11 +193,30 @@ struct PipelineDef PipelineId m_value; }; +inline AnyStageId anystg_from(TaskGraph const& graph, PipelineId const pl, StageId stg) noexcept +{ + return AnyStageId(uint32_t(graph.pipelineToFirstAnystg[pl]) + uint32_t(stg)); +} + +inline StageId stage_from(TaskGraph const& graph, PipelineId const pl, AnyStageId const stg) noexcept +{ + return StageId(uint32_t(stg) - uint32_t(graph.pipelineToFirstAnystg[pl])); +} + +inline StageId stage_from(TaskGraph const& graph, AnyStageId const stg) noexcept +{ + return stage_from(graph, graph.anystgToPipeline[stg], stg); +} + constexpr StageId stage_next(StageId const in, int stageCount) noexcept { int const next = int(in) + 1; return StageId( (next == stageCount) ? 0 : next ); } +constexpr StageId stage_prev(StageId const in, int stageCount) noexcept +{ + return StageId( (int(in)==0) ? (stageCount-1) : (int(in)-1) ); +} } // namespace osp diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 271f0f6b..2d4b13e1 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -83,7 +83,7 @@ struct Pipelines } // namespace test_a // Test pipeline consisting of parallel tasks -TEST(Tasks, BasicParallelSingleThreaded) +TEST(Tasks, BasicSingleThreadedParallelTasks) { using namespace test_a; using enum Stages; @@ -93,7 +93,7 @@ TEST(Tasks, BasicParallelSingleThreaded) // well-suited for this problem, as these per-thread vectors can all be represented with the // same TargetId. - using BasicTraits_t = BasicBuilderTraits&)>; + using BasicTraits_t = BasicBuilderTraits&, int&)>; using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; @@ -116,7 +116,7 @@ TEST(Tasks, BasicParallelSingleThreaded) builder.task() .run_on ({{pl.vec, Fill}}) .triggers({{pl.vec, Clear}}) - .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t + .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> FulfillDirty_t { rOut.push_back(in); return {{0b01}}; @@ -126,20 +126,21 @@ TEST(Tasks, BasicParallelSingleThreaded) // Use vector builder.task() .run_on({{pl.vec, Use}}) - .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t + .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> FulfillDirty_t { int const sum = std::accumulate(rOut.begin(), rOut.end(), 0); EXPECT_EQ(sum, in * sc_pusherTaskCount); + ++rChecksRun; return {{0b0}}; }); // Clear vector after use builder.task() .run_on({{pl.vec, Clear}}) - .func( [] (int const in, std::vector& rOut) -> FulfillDirty_t + .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> FulfillDirty_t { rOut.clear(); - return {{0b01}}; + return {{0b0}}; }); // Step 2: Compile tasks into an execution graph @@ -151,6 +152,7 @@ TEST(Tasks, BasicParallelSingleThreaded) ExecContext exec; exec.resize(tasks); + int checksRun = 0; int input = 0; std::vector output; @@ -162,14 +164,133 @@ TEST(Tasks, BasicParallelSingleThreaded) set_dirty(exec, pl.vec, Fill); enqueue_dirty(tasks, graph, exec); - randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output] (TaskId const task) -> FulfillDirty_t + randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output, &checksRun] (TaskId const task) -> FulfillDirty_t { - return functions[task](input, output); + return functions[task](input, output, checksRun); }); } + + ASSERT_EQ(checksRun, sc_repetitions); } +//----------------------------------------------------------------------------- + +namespace test_b +{ + +struct TestState +{ + int checks {0}; + bool normalFlag {false}; + bool optionalFlagExpect {false}; + bool optionalFlag {false}; +}; + +enum class Stages { Write, Read, Clear }; + +struct Pipelines +{ + osp::PipelineDef normal; + osp::PipelineDef optional; +}; + +} // namespace test_gameworld + + +// +TEST(Tasks, BasicSingleThreadedTriggers) +{ + using namespace test_b; + using enum Stages; + + using BasicTraits_t = BasicBuilderTraits; + using Builder_t = BasicTraits_t::Builder; + using TaskFuncVec_t = BasicTraits_t::FuncVec_t; + + constexpr int sc_repetitions = 128; + std::mt19937 randGen(69); + + Tasks tasks; + TaskEdges edges; + TaskFuncVec_t functions; + Builder_t builder{tasks, edges, functions}; + + auto const pl = builder.create_pipelines(); + + + builder.task() + .run_on ({{pl.normal, Write}}) + .triggers ({{pl.normal, Read}, {pl.normal, Clear}, {pl.optional, Write}}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + { + rState.normalFlag = true; + + if (rRand() % 2 == 0) + { + return {{0b011}}; + } + else + { + rState.optionalFlagExpect = true; + return {{0b111}}; + } + }); + + builder.task() + .run_on ({{pl.normal, Read}}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + { + EXPECT_TRUE(rState.normalFlag); + EXPECT_EQ(rState.optionalFlagExpect, rState.optionalFlag); + return {{0}}; + }); + + builder.task() + .run_on ({{pl.normal, Clear}}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + { + ++ rState.checks; + rState.normalFlag = false; + rState.optionalFlagExpect = false; + rState.optionalFlag = false; + return {{0}}; + }); + + // optional pipeline + builder.task() + .run_on ({{pl.optional, Write}}) + .triggers ({{pl.normal, Read}}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + { + rState.optionalFlag = true; + return {{0b1}}; + }); + + + TaskGraph const graph = make_exec_graph(tasks, {&edges}); + + // Execute + + ExecContext exec; + exec.resize(tasks); + + TestState world; + + set_dirty(exec, pl.normal, Write); + enqueue_dirty(tasks, graph, exec); + + randomized_singlethreaded_execute( + tasks, graph, exec, randGen, 50, + [&functions, &world, &randGen] (TaskId const task) -> FulfillDirty_t + { + return functions[task](world, randGen); + }); + + ASSERT_GT(world.checks, 0); + +} + //----------------------------------------------------------------------------- namespace test_gameworld @@ -196,8 +317,9 @@ struct Pipelines } // namespace test_gameworld + // Single-threaded test against World with order-dependent tasks -TEST(Tasks, BasicSingleThreaded) +TEST(Tasks, BasicSingleThreadedGameWorld) { using namespace test_gameworld; using enum StgSimple; @@ -302,8 +424,6 @@ TEST(Tasks, BasicSingleThreaded) ASSERT_TRUE(world.m_canvas.contains("Physics Cube")); ASSERT_TRUE(world.m_canvas.contains("Terrain")); - - std::cout << "done!\n"; } } From ca55487b8916e35a6c72fea36f73c8220c072655 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 18 Jun 2023 18:10:13 -0700 Subject: [PATCH 18/35] Make pipelines work --- src/osp/tasks/execute.cpp | 461 ++++++++++++++++++++------------------ src/osp/tasks/execute.h | 64 ++---- src/osp/tasks/tasks.cpp | 109 +++++---- src/osp/tasks/tasks.h | 101 +++++++-- test/tasks/main.cpp | 94 ++++---- 5 files changed, 458 insertions(+), 371 deletions(-) diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 72604146..6fd9036d 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -28,253 +28,284 @@ namespace osp { -static bool eval_stage_enter_req(ExecContext &rExec, std::vector const& reqirements) +void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) { - bool out = true; + std::size_t const maxTasks = tasks.m_taskIds.capacity(); + std::size_t const maxPipeline = tasks.m_pipelineIds.capacity(); - for (auto const [reqTask, reqPipeline, reqStage] : reqirements) + rOut.tasksQueuedRun .reserve(maxTasks); + rOut.tasksQueuedBlocked.reserve(maxTasks); + bitvector_resize(rOut.tasksTryRun, maxTasks); + + rOut.plData.resize(maxPipeline); + bitvector_resize(rOut.plDirty, maxPipeline); + + rOut.plNextStage.resize(maxPipeline); + bitvector_resize(rOut.plDirtyNext, maxPipeline); + + rOut.anystgReqByTaskCount.resize(graph.anystgToPipeline.size(), 0); +} + +static StageId pipeline_next_stage(TaskGraph const& graph, ExecContext const &exec, ExecPipeline const &execPl, PipelineId const pipeline) noexcept +{ + StageId out = execPl.currentStage; + + if ( execPl.tasksQueuedBlocked != 0 + || execPl.tasksQueuedRun != 0 + || execPl.stageReqTaskCount != 0) { - ExecPipeline &rReqExecPl = rExec.m_plData[reqPipeline]; - bool const pipelineDirty = rExec.m_plDirty.test(std::size_t(reqPipeline)); - bool const stageMatches = rReqExecPl.m_currentStage == reqStage; - bool const taskRunning = rExec.m_tasksQueuedRun .contains(reqTask); - bool const taskBlocked = rExec.m_tasksQueuedBlocked.contains(reqTask); - bool const allTasksDone = (rReqExecPl.m_tasksQueuedRun == 0) - && (rReqExecPl.m_tasksQueuedBlocked == 0); + return out; // Can't advance. This stage still has (or requires other) tasks to complete + } - // All required Pipelines must be on the correct stage to allow incrementing stage - out &= stageMatches; + int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); - // All required Tasks must not be in-progress (completed) to allow incrementing stage - out &= ! (taskRunning || taskBlocked); + while(true) + { + if (exec.anystgReqByTaskCount[anystg_from(graph, pipeline, out)]) + { + return out; // Can't advance, there are queued tasks that require this stage + } - if (taskBlocked) + out = stage_next(out, stageCount); + + if (execPl.triggered.test(std::size_t(out))) { - rExec.m_tasksTryRun.set(std::size_t(reqTask)); + return out; // Stop on this stage to run tasks } - if ( ( ! stageMatches ) && ( ! pipelineDirty ) && allTasksDone ) + // No triggered and no waiting tasks means infinite loop. Stage should still move by 1 though. + if ( ( ! execPl.triggered.any() ) + && ( execPl.stageReqTaskCount == 0 ) + && ( execPl.taskReqStageCount == 0 ) ) { - // Pipeline is not running tasks and not on the right stage. We need it to advance even - // though it has no tasks to run. For this, mark it as 'required' to be set dirty later - rExec.m_plRequired.set(std::size_t(reqPipeline)); + return out; } } - - return out; } -//static void advance_pipeline(ExecContext &rExec, TaskGraphPipeline const &graphPl, ExecPipeline &rExecPl, PipelineId const pipeline, StageId& rNextStage) -//{ -// auto const stageCount = int(graphPl.m_stages.size()); +static void apply_pipeline_stage(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +{ + ExecPipeline &rPlExec = rExec.plData[pipeline]; + StageId &rStage = rPlExec.currentStage; + StageId const oldStage = rStage; + StageId const newStage = rExec.plNextStage[pipeline]; -// rNextStage = rExecPl.m_currentStage; + if (oldStage == newStage) + { + return; // no change + } -// while(true) -// { -// auto const nextStageInt = int(rNextStage); + rStage = newStage; -// bool const triggered = rExecPl.m_triggered .test(nextStageInt); -// bool const waited = rExecPl.m_stageCounts[nextStageInt].m_waitingTasks != 0; + LGRN_ASSERTV(rPlExec.stageReqTaskCount == 0, rPlExec.stageReqTaskCount); -// StageId const nextNextStage = stage_next(rNextStage, stageCount); -// bool const canIncrement = eval_stage_enter_req(rExec, graphPl.m_stages[nextNextStage].m_enterReq); + // Evaluate Stage-requires-Tasks + // * Calculate stageReqTaskCount as the number of required task that are currently queued + AnyStageId const anystg = anystg_from(graph, pipeline, newStage); + int stageReqTaskCount = 0; -// if ( ( ! canIncrement ) || triggered || waited ) -// { -// return; -// } + for (StageRequiresTask const& stgreqtask : fanout_view(graph.anystgToFirstStgreqtask, graph.stgreqtaskData, anystg)) + { + if (rExec.tasksQueuedBlocked.contains(stgreqtask.reqTask) + || rExec.tasksQueuedRun .contains(stgreqtask.reqTask)) + { + ++ stageReqTaskCount; + } + } -// rNextStage = nextNextStage; + rPlExec.stageReqTaskCount = stageReqTaskCount; -// // No triggered and no waiting tasks means infinite loop. Stage should still move by 1 though. -// if ( ( ! rExecPl.m_triggered.any() ) -// && ( rExecPl.m_waitingTasksTotal == 0 ) ) -// { -// return; -// } -// } -//} + // Evaluate Task-requires-Stages + // * Increment counts for queued tasks that depend on this stage. This unblocks tasks -static void update_pipeline(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) + for (TaskId const& task : fanout_view(graph.anystgToFirstRevTaskreqstg, graph.revTaskreqstgToTask, anystg)) + { + if (rExec.tasksQueuedBlocked.contains(task)) + { + BlockedTask &rBlocked = rExec.tasksQueuedBlocked.get(task); + -- rBlocked.remainingTaskReqStg; + if (rBlocked.remainingTaskReqStg == 0) + { + ExecPipeline &rTaskPlExec = rExec.plData[rBlocked.pipeline]; + -- rTaskPlExec.tasksQueuedBlocked; + ++ rTaskPlExec.tasksQueuedRun; + rExec.tasksQueuedRun.emplace(task); + rExec.tasksQueuedBlocked.erase(task); + } + } + } +} + +static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { -// ExecPipeline &rPlExec = rExec.m_plData[pipeline]; -// StageId const stage = rExec.m_plNextStage[pipeline]; -// StageBits_t const stageBit = 1 << int(stage); - -// rPlExec.m_currentStage = stage; - -// if ( (rPlExec.m_triggered & stageBit) == 0 ) -// { -// return; // Not triggered -// } - -// rPlExec.m_triggered ^= stageBit; - -// // Enqueue all tasks - -// auto const& runTasks = graph.m_pipelines[pipeline].m_stages[stage].m_runTasks; - -// for (TaskId task : runTasks) -// { -// bool blocked = false; -// for (auto const [waitPipeline, waitStage] : graph.m_taskSync[TaskInt(task)]) -// { -// ExecPipeline &rWaitPlExec = rExec.m_plData[waitPipeline]; -// ++ rWaitPlExec.m_stageCounts[std::size_t(waitStage)].m_waitingTasks; -// ++ rWaitPlExec.m_waitingTasksTotal; - -// if (rWaitPlExec.m_currentStage != waitStage) -// { -// blocked = true; -// } -// } - -// if (blocked) -// { -// ++rPlExec.m_tasksQueuedBlocked; -// rExec.m_tasksQueuedBlocked.emplace(task); -// } -// else -// { -// ++rPlExec.m_tasksQueuedRun; -// rExec.m_tasksQueuedRun.emplace(task); -// } -// } + ExecPipeline &rPlExec = rExec.plData[pipeline]; + StageId const stage = rExec.plNextStage[pipeline]; + StageBits_t const stageBit = 1 << int(stage); + + LGRN_ASSERT(rPlExec.tasksQueuedBlocked == 0); + LGRN_ASSERT(rPlExec.tasksQueuedRun == 0); + + if ( (rPlExec.triggered & stageBit) == 0 ) + { + return; // Not triggered + } + + rPlExec.triggered &= ~stageBit; + + // Enqueue all tasks + + AnyStageId const anystg = anystg_from(graph, pipeline, stage); + + for (TaskId task : fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)) + { + // Evaluate Stage-requires-Tasks + // * Increment counts for currently running stages that require this task + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) + { + PipelineId const reqTaskPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqTaskStg = stage_from(graph, reqTaskPl, reqTaskAnystg); + ExecPipeline &rReqTaskPlData = rExec.plData[reqTaskPl]; + + if (rReqTaskPlData.currentStage == reqTaskStg) + { + ++ rReqTaskPlData.stageReqTaskCount; + } + } + + // Evaluate Task-requires-Stages + // * Increment counts for each required stage + // * Determine which requirements are already satisfied + auto const taskreqstageView = ArrayView(fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)); + int reqsRemaining = taskreqstageView.size(); + for (TaskRequiresStage const& req : taskreqstageView) + { + AnyStageId const reqAnystg = anystg_from(graph, req.reqPipeline, req.reqStage); + ExecPipeline &rReqPlData = rExec.plData[req.reqPipeline]; + + ++ rExec.anystgReqByTaskCount[reqAnystg]; + ++ rReqPlData.taskReqStageCount; + + if (rReqPlData.currentStage == req.reqStage) + { + reqsRemaining --; + } + else if ( rReqPlData.tasksQueuedRun == 0 + && rReqPlData.tasksQueuedBlocked == 0 ) + { + rExec.plDirtyNext.set(std::size_t(req.reqPipeline)); + } + } + + if (reqsRemaining != 0) + { + rExec.tasksQueuedBlocked.emplace(task, BlockedTask{reqsRemaining, pipeline}); + ++ rPlExec.tasksQueuedBlocked; + } + else + { + // Task can run right away + rExec.tasksQueuedRun.emplace(task); + ++ rPlExec.tasksQueuedRun; + } + } } void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept { -// auto const task_can_run = [&graph, &rExec] (TaskId const task) -// { -// for (auto const [waitPipeline, waitStage] : graph.m_taskSync[TaskInt(task)]) -// { -// if (rExec.m_plData[waitPipeline].m_currentStage != waitStage) -// { -// return false; -// } -// } - -// return true; -// }; - -// auto const plDirtyOnes = rExec.m_plDirty.ones(); - -// while (plDirtyOnes.begin() != plDirtyOnes.end()) -// { -// // 1. Advance pipelines and determine next stages. Writes to... -// // * rExec.m_plNextStage - Next stages for each dirty pipeline -// // * rExec.m_plRequired - Pipelines that need to be dirty next -// // * rExec.m_tasksTryRun - Blocked tasks that might be able to start -// for (std::size_t const pipelineInt : plDirtyOnes) -// { -// auto const pipeline = PipelineId(pipelineInt); -// ExecPipeline &rPlExec = rExec.m_plData[pipeline]; - -// if ( rPlExec.m_tasksQueuedBlocked == 0 && rPlExec.m_tasksQueuedRun == 0 ) -// { -// advance_pipeline(rExec, -// graph.m_pipelines[pipeline], -// rPlExec, -// pipeline, -// rExec.m_plNextStage[pipeline]); -// } -// } - -// // 2. Apply new stages and enqueue tasks. Writes to... -// // * rExec.m_plData[n] - Pipeline trigger flags and counts for queued tasks -// // * rExec.m_tasksQueuedBlocked - Queued task that are blocked -// // * rExec.m_tasksQueuedRun - Queued task that can run right away -// for (std::size_t const pipelineInt : plDirtyOnes) -// { -// update_pipeline(tasks, graph, rExec, PipelineId(pipelineInt)); -// } - -// // 3. Try running some blocked tasks that might be able to run after pipelines advanced -// for (std::size_t const taskInt : rExec.m_tasksTryRun.ones()) -// { -// auto const task = TaskId(taskInt); -// LGRN_ASSERT(rExec.m_tasksQueuedBlocked.contains(task)); - -// if (task_can_run(task)) -// { -// rExec.m_tasksQueuedBlocked.remove(task); -// rExec.m_tasksQueuedRun.emplace(task); - -// for (auto const [pipeline, stage] : graph.m_taskRunOn[TaskInt(task)]) -// { -// ExecPipeline &plExec = rExec.m_plData[pipeline]; - -// --plExec.m_tasksQueuedBlocked; -// ++plExec.m_tasksQueuedRun; -// } -// } -// } -// rExec.m_tasksTryRun.reset(); - -// // 3. 'Required' pipeline is set as dirty for next iteration. -// std::copy(rExec.m_plRequired.ints().begin(), -// rExec.m_plRequired.ints().end(), -// rExec.m_plDirty .ints().begin()); -// rExec.m_plRequired.reset(); -// } - -// rExec.m_plDirty.reset(); + auto const plDirtyOnes = rExec.plDirty.ones(); + + while (plDirtyOnes.begin() != plDirtyOnes.end()) + { + // Calculate next stages + for (std::size_t const pipelineInt : plDirtyOnes) + { + auto const pipeline = PipelineId(pipelineInt); + ExecPipeline &rPlExec = rExec.plData[pipeline]; + rExec.plNextStage[pipeline] = pipeline_next_stage(graph, rExec, rPlExec, pipeline); + } + + // Apply next stages + for (std::size_t const pipelineInt : plDirtyOnes) + { + apply_pipeline_stage(graph, rExec, PipelineId(pipelineInt)); + } + + // Run tasks. writes to plDirtyNext + for (std::size_t const pipelineInt : plDirtyOnes) + { + run_pipeline_tasks(tasks, graph, rExec, PipelineId(pipelineInt)); + } + + // Select next dirty pipelines + std::copy(rExec.plDirtyNext.ints().begin(), + rExec.plDirtyNext.ints().end(), + rExec.plDirty .ints().begin()); + rExec.plDirtyNext.reset(); + } } void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept { -// LGRN_ASSERT(rExec.m_tasksQueuedRun.contains(task)); -// rExec.m_tasksQueuedRun.erase(task); - -// bool allPipelinesComplete = true; - -// for (auto const [pipeline, stage] : graph.m_taskRunOn[TaskInt(task)]) -// { -// ExecPipeline &plExec = rExec.m_plData[pipeline]; - -// plExec.m_tasksQueuedRun --; -// if (plExec.m_tasksQueuedRun == 0) -// { -// rExec.m_plDirty.set(std::size_t(pipeline)); -// } -// else -// { -// allPipelinesComplete = false; -// } -// } - -// for (auto const [waitPipeline, waitStage] : graph.m_taskSync[TaskInt(task)]) -// { -// ExecPipeline &rWaitPlExec = rExec.m_plData[waitPipeline]; -// auto &rStgWaitingTasks = rWaitPlExec.m_stageCounts[std::size_t(waitStage)].m_waitingTasks; -// -- rStgWaitingTasks; -// -- rWaitPlExec.m_waitingTasksTotal; - -// if (rStgWaitingTasks == 0 && rWaitPlExec.m_tasksQueuedRun == 0 && rWaitPlExec.m_tasksQueuedBlocked == 0) -// { -// rExec.m_plDirty.set(std::size_t(waitPipeline)); -// } -// } - -// if ( ! allPipelinesComplete ) -// { -// return; -// } - -// auto const triggers = lgrn::Span(graph.m_taskTriggers[TaskInt(task)]); - -// for (int i = 0; i < triggers.size(); ++i) -// { -// if (dirty.test(i)) -// { -// auto const [pipeline, stage] = triggers[i]; - -// rExec.m_plData[pipeline].m_triggered |= 1 << int(stage); -// rExec.m_plDirty.set(std::size_t(pipeline)); -// } -// } + LGRN_ASSERT(rExec.tasksQueuedRun.contains(task)); + rExec.tasksQueuedRun.erase(task); + + auto const try_set_dirty = [&rExec] (ExecPipeline &plExec, PipelineId pipeline) + { + if ( plExec.tasksQueuedRun == 0 + && plExec.tasksQueuedBlocked == 0 + && plExec.stageReqTaskCount == 0) + { + rExec.plDirty.set(std::size_t(pipeline)); + } + }; + + // Handle task running on stage + for (AnyStageId const anystg : fanout_view(graph.taskToFirstRunstage, graph.runstageToAnystg, task)) + { + PipelineId const pipeline = graph.anystgToPipeline[anystg]; + ExecPipeline &plExec = rExec.plData[pipeline]; + + -- plExec.tasksQueuedRun; + try_set_dirty(plExec, pipeline); + } + + // Handle stages requiring this task + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) + { + PipelineId const reqTaskPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqTaskStg = stage_from(graph, reqTaskPl, reqTaskAnystg); + ExecPipeline &rReqTaskPlData = rExec.plData[reqTaskPl]; + + if (rReqTaskPlData.currentStage == reqTaskStg) + { + -- rReqTaskPlData.stageReqTaskCount; + + try_set_dirty(rReqTaskPlData, reqTaskPl); + } + } + + // Handle this task requiring stages + for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) + { + AnyStageId const reqAnystg = anystg_from(graph, req.reqPipeline, req.reqStage); + -- rExec.anystgReqByTaskCount[reqAnystg]; + -- rExec.plData[req.reqPipeline].taskReqStageCount; + } + + // Trigger specified stages based on return value + auto const triggersView = ArrayView(fanout_view(graph.taskToFirstTrigger, graph.triggerToPlStage, task)); + for (int i = 0; i < triggersView.size(); ++i) + { + if (dirty.test(i)) + { + auto const [pipeline, stage] = triggersView[i]; + ExecPipeline &plExec = rExec.plData[pipeline]; + + plExec.triggered |= 1 << int(stage); + try_set_dirty(plExec, pipeline); + } + } } } // namespace osp diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index ddb859f6..70128020 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include @@ -39,27 +39,22 @@ namespace osp { -struct StageCounts -{ - uint16_t m_waitingTasks {0}; -}; - struct ExecPipeline { - StageBits_t m_triggered {0}; - int m_tasksQueuedRun {0}; - int m_tasksQueuedBlocked{0}; - StageId m_currentStage {0}; - uint32_t m_waitingTasksTotal {0}; + StageBits_t triggered {0}; + StageId currentStage {0}; - std::array m_stageCounts; + int tasksQueuedRun {0}; + int tasksQueuedBlocked {0}; + + int stageReqTaskCount {0}; + int taskReqStageCount {0}; }; -struct TaskWaiting +struct BlockedTask { - TaskId m_recipient; - PipelineId m_waitFor; - StageId m_waitForStage; + int remainingTaskReqStg; + PipelineId pipeline; }; /** @@ -71,33 +66,20 @@ struct TaskWaiting */ struct ExecContext { - void resize(Tasks const& tasks) - { - std::size_t const maxTasks = tasks.m_taskIds.capacity(); - std::size_t const maxPipeline = tasks.m_pipelineIds.capacity(); - - m_tasksQueuedRun .reserve(maxTasks); - m_tasksQueuedBlocked.reserve(maxTasks); - bitvector_resize(m_tasksTryRun, maxTasks); + KeyedVec plData; + BitVector_t plDirty; + BitVector_t plDirtyNext; - m_plData.resize(maxPipeline); - bitvector_resize(m_plDirty, maxPipeline); + entt::basic_sparse_set tasksQueuedRun; + entt::basic_storage tasksQueuedBlocked; + BitVector_t tasksTryRun; - m_plNextStage.resize(maxPipeline); - bitvector_resize(m_plRequired, maxPipeline); - } - - KeyedVec m_plData; - BitVector_t m_plDirty; - - entt::basic_sparse_set m_tasksQueuedRun; - entt::basic_sparse_set m_tasksQueuedBlocked; - BitVector_t m_tasksTryRun; + KeyedVec anystgReqByTaskCount; // used for updating - BitVector_t m_plRequired; - KeyedVec m_plNextStage; + + KeyedVec plNextStage; // TODO: Consider multithreading. something something work stealing... // * Allow multiple threads to search for and execute tasks. Atomic access @@ -110,6 +92,8 @@ struct ExecContext }; // struct ExecContext +void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); + //inline bool compare_syncs(SyncWaiting const& lhs, SyncWaiting const& rhs) //{ // return (lhs.m_recipient < rhs.m_recipient) @@ -119,8 +103,8 @@ struct ExecContext template inline void set_dirty(ExecContext &rExec, PipelineDef const pipeline, STAGE_ENUM_T const stage) { - rExec.m_plData[pipeline].m_triggered |= 1 << int(stage); - rExec.m_plDirty.set(std::size_t(pipeline)); + rExec.plData[pipeline].triggered |= 1 << int(stage); + rExec.plDirty.set(std::size_t(pipeline)); } void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index 6b561f0f..2abc05d7 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -24,8 +24,6 @@ */ #include "tasks.h" -#include "Corrade/Containers/ArrayViewStl.h" - #include namespace osp @@ -33,10 +31,10 @@ namespace osp struct TaskCounts { - uint8_t runOn {0}; uint16_t requiresStages {0}; uint16_t requiredByStages {0}; - + uint16_t triggers {0}; + uint8_t runOn {0}; }; struct StageCounts @@ -48,44 +46,10 @@ struct StageCounts struct PipelineCounts { - uint8_t stages {0}; std::array stageCounts; -}; - - - -template -static void fill_many(KeyedVec& rVec, GETSIZE_T&& get_size, CLAIM_T&& claim) -{ - using key_int_t = lgrn::underlying_int_type_t; - using value_int_t = lgrn::underlying_int_type_t; - - value_int_t currentId = 0; - for (value_int_t i = 0; i < rVec.size(); ++i) - { - value_int_t const size = get_size(KEY_T(i)); - - rVec[KEY_T(i)] = VALUE_T(currentId); - - if (size != 0) - { - value_int_t const nextId = currentId + size; - - for (value_int_t j = currentId; j < nextId; ++j) - { - claim(KEY_T(i), VALUE_T(j)); - } - - currentId = nextId; - } - } -} -template -static VALUE_T id_from_count(KeyedVec const& vec, KEY_T const key, lgrn::underlying_int_type_t const count) -{ - return VALUE_T( lgrn::underlying_int_type_t(vec[KEY_T(lgrn::underlying_int_type_t(key) + 1)]) - count ); -} + uint8_t stages {0}; +}; TaskGraph make_exec_graph(Tasks const& tasks, ArrayView const data) @@ -106,6 +70,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView std::size_t totalTasksReqStage = 0; std::size_t totalStageReqTasks = 0; std::size_t totalRunTasks = 0; + std::size_t totalTriggers = 0; std::size_t totalStages = 0; // Count up which pipeline/stages each task runs on @@ -119,11 +84,10 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView for (TaskEdges const* pEdges : data) { totalRunTasks += pEdges->m_runOn.size(); - for (auto const [task, pipeline, stage] : pEdges->m_runOn) { plCounts[pipeline].stageCounts[std::size_t(stage)].runTasks ++; - taskCounts[task] .runOn ++; + taskCounts[task].runOn ++; count_stage(pipeline, stage); } @@ -132,8 +96,10 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView count_stage(pipeline, stage); } + totalTriggers += pEdges->m_triggers.size(); for (auto const [task, pipeline, stage] : pEdges->m_triggers) { + taskCounts[task].triggers ++; count_stage(pipeline, stage); } } @@ -192,27 +158,34 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView // Allocate + // The +1 is needed for 1-to-many connections to store the total number of other elements they + // index. This also simplifies logic in fanout_view(...) + out.pipelineToFirstAnystg .resize(maxPipelines+1, lgrn::id_null()); out.anystgToPipeline .resize(totalStages+1, lgrn::id_null()); out.anystgToFirstRuntask .resize(totalStages+1, lgrn::id_null()); out.runtaskToTask .resize(totalRunTasks, lgrn::id_null()); + out.taskToFirstRunstage .resize(maxTasks+1, lgrn::id_null()); + out.runstageToAnystg .resize(totalRunTasks, lgrn::id_null()); + out.taskToFirstTrigger .resize(maxTasks+1, lgrn::id_null()); + out.triggerToPlStage .resize(totalTriggers, {lgrn::id_null(), lgrn::id_null()}); out.anystgToFirstStgreqtask .resize(totalStages+1, lgrn::id_null()); out.stgreqtaskData .resize(totalStageReqTasks, {}); out.taskToFirstRevStgreqtask .resize(maxTasks+1, lgrn::id_null()); out.revStgreqtaskToStage .resize(totalStageReqTasks, lgrn::id_null()); out.taskToFirstTaskreqstg .resize(maxTasks+1, lgrn::id_null()); out.taskreqstgData .resize(totalTasksReqStage, {}); - out.stageToFirstRevTaskreqstg .resize(totalStages, lgrn::id_null()); + out.anystgToFirstRevTaskreqstg .resize(totalStages+1, lgrn::id_null()); out.revTaskreqstgToTask .resize(totalTasksReqStage, lgrn::id_null()); - // Put spaces + // Calcualte one-to-many partitions - fill_many( + fanout_partition( out.pipelineToFirstAnystg, [&plCounts] (PipelineId pl) { return plCounts[pl].stages; }, [&out] (PipelineId pl, AnyStageId claimed) { out.anystgToPipeline[claimed] = pl; }); - fill_many( + fanout_partition( out.anystgToFirstRuntask, [&plCounts, &out] (AnyStageId stg) { @@ -226,8 +199,17 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView }, [] (AnyStageId, RunTaskId) { }); - // for StageReqTaskId - fill_many( + fanout_partition( + out.taskToFirstRunstage, + [&taskCounts, &out] (TaskId task) { return taskCounts[task].runOn; }, + [&out] (TaskId, RunStageId) { }); + + fanout_partition( + out.taskToFirstTrigger, + [&taskCounts, &out] (TaskId task) { return taskCounts[task].triggers; }, + [&out] (TaskId, TriggerId) { }); + + fanout_partition( out.anystgToFirstStgreqtask, [&plCounts, &out] (AnyStageId stg) { @@ -240,21 +222,24 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView return plCounts[pl].stageCounts[std::size_t(stgLocal)].requiresTasks; }, [&out] (AnyStageId stg, StageReqTaskId claimed) { out.stgreqtaskData[claimed].ownStage = stg; }); - fill_many( + fanout_partition( out.taskToFirstRevStgreqtask, [&taskCounts] (TaskId task) { return taskCounts[task].requiredByStages; }, [&out] (TaskId, ReverseStageReqTaskId) { }); - // for TaskReqStage - fill_many( + fanout_partition( out.taskToFirstTaskreqstg, [&taskCounts] (TaskId task) { return taskCounts[task].requiresStages; }, [&out] (TaskId task, TaskReqStageId claimed) { out.taskreqstgData[claimed].ownTask = task; }); - fill_many( - out.stageToFirstRevTaskreqstg, + fanout_partition( + out.anystgToFirstRevTaskreqstg, [&plCounts, &out] (AnyStageId stg) { PipelineId const pl = out.anystgToPipeline[stg]; + if (pl == lgrn::id_null()) + { + return uint16_t(0); + } StageId const stgLocal = stage_from(out, pl, stg); return plCounts[pl].stageCounts[std::size_t(stgLocal)].requiredByTasks; }, @@ -290,7 +275,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView TaskCounts &rTaskCounts = taskCounts[task]; TaskReqStageId const taskReqStgId = id_from_count(out.taskToFirstTaskreqstg, task, rTaskCounts.requiresStages); - ReverseTaskReqStageId const revTaskReqStgId = id_from_count(out.stageToFirstRevTaskreqstg, anystg, rStageCounts.requiredByTasks); + ReverseTaskReqStageId const revTaskReqStgId = id_from_count(out.anystgToFirstRevTaskreqstg, anystg, rStageCounts.requiredByTasks); TaskRequiresStage &rTaskReqStage = out.taskreqstgData[taskReqStgId]; rTaskReqStage.reqStage = stg; @@ -311,7 +296,10 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView TaskCounts &rTaskCounts = taskCounts[task]; RunTaskId const runTask = id_from_count(out.anystgToFirstRuntask, anystg, rStageCounts.runTasks); + RunStageId const runStage = id_from_count(out.taskToFirstRunstage, task, rTaskCounts.runOn); + out.runtaskToTask[runTask] = task; + out.runstageToAnystg[runStage] = anystg; -- rStageCounts.runTasks; -- rTaskCounts.runOn; @@ -327,11 +315,22 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView for (auto const [task, pipeline, stage] : pEdges->m_triggers) { add_stagereqtask(pipeline, stage_prev(stage, plCounts[pipeline].stages), task); + + TaskCounts &rTaskCounts = taskCounts[task]; + TriggerId const trigger = id_from_count(out.taskToFirstTrigger, task, rTaskCounts.triggers); + out.triggerToPlStage[trigger] = { pipeline, stage }; + -- rTaskCounts.triggers; } } - [[maybe_unused]] auto const all_counts_zero = [&plCounts, &taskCounts] () + [[maybe_unused]] auto const all_counts_zero = [&] () { + if ( totalRunTasks != 0 + || totalStageReqTasks != 0 + || totalTasksReqStage != 0) + { + return false; + } for (PipelineCounts const& plCount : plCounts) { for (StageCounts const& stgCount : plCount.stageCounts) diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 1123501f..a5b1ae36 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -88,21 +88,11 @@ struct TaskEdges std::vector m_semaphoreEdges; }; -//struct TaskGraphStage -//{ -// std::vector m_runTasks; -// std::vector m_triggeredBy; -// std::vector m_syncedBy; -// std::vector m_enterReq; -//}; - -//struct TaskGraphPipeline -//{ -// KeyedVec m_stages; -//}; - enum class AnyStageId : uint32_t { }; + enum class RunTaskId : uint32_t { }; +enum class RunStageId : uint32_t { }; +enum class TriggerId : uint32_t { }; enum class StageReqTaskId : uint32_t { }; enum class ReverseStageReqTaskId : uint32_t { }; @@ -143,6 +133,16 @@ struct TaskGraph KeyedVec anystgToFirstRuntask; KeyedVec runtaskToTask; + // Each task runs on many stages + // TaskId --> TaskRunStageId --> many AnyStageId + KeyedVec taskToFirstRunstage; + KeyedVec runstageToAnystg; + + // Tasks trigger stages of pipelines + // TaskId --> TriggerId --> many TplPipelineStage + KeyedVec taskToFirstTrigger; + KeyedVec triggerToPlStage; + // Each stage has multiple entrance requirements. // AnyStageId <--> many StageEnterReqId KeyedVec anystgToFirstStgreqtask; @@ -152,14 +152,13 @@ struct TaskGraph KeyedVec taskToFirstRevStgreqtask; KeyedVec revStgreqtaskToStage; - // Task requires pipelines to be on certain stages. // TaskId <--> TaskReqId KeyedVec taskToFirstTaskreqstg; KeyedVec taskreqstgData; // Stages need to know which tasks require them // StageId --> ReverseTaskReqId --> many TaskId - KeyedVec stageToFirstRevTaskreqstg; + KeyedVec anystgToFirstRevTaskreqstg; KeyedVec revTaskreqstgToTask; @@ -182,16 +181,63 @@ inline TaskGraph make_exec_graph(Tasks const& tasks, std::initializer_list -struct PipelineDef +template +inline void fanout_partition(KeyedVec& rVec, GETSIZE_T&& get_size, CLAIM_T&& claim) noexcept { - operator PipelineId() const noexcept { return m_value; } - operator std::size_t() const noexcept { return std::size_t(m_value); } + using key_int_t = lgrn::underlying_int_type_t; + using value_int_t = lgrn::underlying_int_type_t; - PipelineId& operator=(PipelineId const assign) { m_value = assign; return m_value; } + value_int_t currentId = 0; + for (value_int_t i = 0; i < rVec.size(); ++i) + { + value_int_t const size = get_size(KEY_T(i)); - PipelineId m_value; -}; + rVec[KEY_T(i)] = VALUE_T(currentId); + + if (size != 0) + { + value_int_t const nextId = currentId + size; + + for (value_int_t j = currentId; j < nextId; ++j) + { + claim(KEY_T(i), VALUE_T(j)); + } + + currentId = nextId; + } + } +} + +template +inline auto fanout_size(KeyedVec const& vec, KEY_T key) -> lgrn::underlying_int_type_t +{ + using key_int_t = lgrn::underlying_int_type_t; + using value_int_t = lgrn::underlying_int_type_t; + + value_int_t const firstIdx = value_int_t(vec[key]); + value_int_t const lastIdx = value_int_t(vec[KEY_T(key_int_t(key) + 1)]); + + return lastIdx - firstIdx; +} + +template +inline decltype(auto) fanout_view(KeyedVec const& vec, KeyedVec const& access, KEY_T key) +{ + using key_int_t = lgrn::underlying_int_type_t; + using value_int_t = lgrn::underlying_int_type_t; + + value_int_t const firstIdx = value_int_t(vec[key]); + value_int_t const lastIdx = value_int_t(vec[KEY_T(key_int_t(key) + 1)]); + + return arrayView(access.data(), access.size()).slice(firstIdx, lastIdx); +} + + +template +inline VALUE_T id_from_count(KeyedVec const& vec, KEY_T const key, lgrn::underlying_int_type_t const count) +{ + return VALUE_T( lgrn::underlying_int_type_t(vec[KEY_T(lgrn::underlying_int_type_t(key) + 1)]) - count ); +} inline AnyStageId anystg_from(TaskGraph const& graph, PipelineId const pl, StageId stg) noexcept { @@ -219,4 +265,15 @@ constexpr StageId stage_prev(StageId const in, int stageCount) noexcept return StageId( (int(in)==0) ? (stageCount-1) : (int(in)-1) ); } +template +struct PipelineDef +{ + operator PipelineId() const noexcept { return m_value; } + operator std::size_t() const noexcept { return std::size_t(m_value); } + + PipelineId& operator=(PipelineId const assign) { m_value = assign; return m_value; } + + PipelineId m_value; +}; + } // namespace osp diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 2d4b13e1..688219e7 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -53,17 +53,21 @@ void randomized_singlethreaded_execute(Tasks const& tasks, TaskGraph const& grap { for (int i = 0; i < maxRuns; ++i) { - std::size_t const tasksLeft = rExec.m_tasksQueuedRun.size(); + auto const runTasksLeft = rExec.tasksQueuedRun.size(); + auto const blockedTasksLeft = rExec.tasksQueuedBlocked.size(); - if (tasksLeft == 0) + if (runTasksLeft+blockedTasksLeft == 0) { break; } - TaskId const randomTask = rExec.m_tasksQueuedRun.at(rRand() % tasksLeft); + if (runTasksLeft != 0) + { + TaskId const randomTask = rExec.tasksQueuedRun.at(rRand() % runTasksLeft); + FulfillDirty_t const status = runTask(randomTask); + mark_completed_task(tasks, graph, rExec, randomTask, status); + } - FulfillDirty_t const status = runTask(randomTask); - mark_completed_task(tasks, graph, rExec, randomTask, status); enqueue_dirty(tasks, graph, rExec); } } @@ -87,6 +91,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) { using namespace test_a; using enum Stages; + // NOTE // If this was multithreaded, then multiple threads writing to a single container is a bad // idea. The proper way to do this is to make a vector per-thread. Targets are still @@ -99,7 +104,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) constexpr int sc_repetitions = 32; constexpr int sc_pusherTaskCount = 24; - constexpr int sc_totalTaskCount = sc_pusherTaskCount + 1; + constexpr int sc_totalTaskCount = sc_pusherTaskCount + 2; std::mt19937 randGen(69); // Step 1: Create tasks @@ -115,11 +120,11 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) { builder.task() .run_on ({{pl.vec, Fill}}) - .triggers({{pl.vec, Clear}}) + .triggers({{pl.vec, Use}, {pl.vec, Clear}}) .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> FulfillDirty_t { rOut.push_back(in); - return {{0b01}}; + return {{0b11}}; }); } @@ -150,7 +155,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) // Step 3: Run ExecContext exec; - exec.resize(tasks); + exec_resize(tasks, graph, exec); int checksRun = 0; int input = 0; @@ -181,13 +186,13 @@ namespace test_b struct TestState { - int checks {0}; - bool normalFlag {false}; - bool optionalFlagExpect {false}; - bool optionalFlag {false}; + int checks { 0 }; + bool normalFlag { false }; + bool optionalFlagExpect { false }; + bool optionalFlag { false }; }; -enum class Stages { Write, Read, Clear }; +enum class Stages { Schedule, Write, Read, Clear }; struct Pipelines { @@ -198,7 +203,7 @@ struct Pipelines } // namespace test_gameworld -// +// Test that features a looping 'normal' pipeline and an 'optional' pipeline that has a 50% chance of running TEST(Tasks, BasicSingleThreadedTriggers) { using namespace test_b; @@ -208,7 +213,7 @@ TEST(Tasks, BasicSingleThreadedTriggers) using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; - constexpr int sc_repetitions = 128; + constexpr int sc_taskRuns = 128; std::mt19937 randGen(69); Tasks tasks; @@ -218,76 +223,87 @@ TEST(Tasks, BasicSingleThreadedTriggers) auto const pl = builder.create_pipelines(); + // These tasks run in a loop, triggering each other in a loop builder.task() - .run_on ({{pl.normal, Write}}) - .triggers ({{pl.normal, Read}, {pl.normal, Clear}, {pl.optional, Write}}) + .run_on ({{pl.normal, Schedule}}) + .triggers ({{pl.normal, Write}, {pl.optional, Write}}) .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t { - rState.normalFlag = true; - if (rRand() % 2 == 0) { - return {{0b011}}; + return {{0b01}}; } else { rState.optionalFlagExpect = true; - return {{0b111}}; + return {{0b11}}; } }); + builder.task() + .run_on ({{pl.normal, Write}}) + .triggers ({{pl.normal, Read}, {pl.normal, Clear}}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + { + rState.normalFlag = true; + return {{0b11}}; + }); + + builder.task() + .run_on ({{pl.optional, Write}}) + .triggers ({{pl.optional, Read}, {pl.optional, Clear}}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + { + rState.optionalFlag = true; + return {{0b11}}; + }); + + builder.task() .run_on ({{pl.normal, Read}}) + .sync_with({{pl.optional, Read}}) .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t { EXPECT_TRUE(rState.normalFlag); EXPECT_EQ(rState.optionalFlagExpect, rState.optionalFlag); - return {{0}}; + return {{0b1}}; }); builder.task() .run_on ({{pl.normal, Clear}}) + .triggers ({{pl.normal, Schedule}}) .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t { ++ rState.checks; rState.normalFlag = false; rState.optionalFlagExpect = false; rState.optionalFlag = false; - return {{0}}; - }); - - // optional pipeline - builder.task() - .run_on ({{pl.optional, Write}}) - .triggers ({{pl.normal, Read}}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t - { - rState.optionalFlag = true; return {{0b1}}; }); - TaskGraph const graph = make_exec_graph(tasks, {&edges}); // Execute ExecContext exec; - exec.resize(tasks); + exec_resize(tasks, graph, exec); TestState world; - set_dirty(exec, pl.normal, Write); + set_dirty(exec, pl.normal, Schedule); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute( - tasks, graph, exec, randGen, 50, + tasks, graph, exec, randGen, sc_taskRuns, [&functions, &world, &randGen] (TaskId const task) -> FulfillDirty_t { return functions[task](world, randGen); }); - ASSERT_GT(world.checks, 0); + // Assure that the tasks above actually ran, and didn't just skip everything + // Max of 5 tasks run each loop + ASSERT_GT(world.checks, sc_taskRuns / 5); } @@ -398,7 +414,7 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Execute ExecContext exec; - exec.resize(tasks); + exec_resize(tasks, graph, exec); World world; From 671aff9ddec9f69ff3a54f7fc05d120c55942d4c Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Wed, 21 Jun 2023 10:54:26 -0700 Subject: [PATCH 19/35] Fix enginetest for Pipelines --- src/osp/tasks/builder.h | 6 +- src/osp/tasks/execute.cpp | 2 +- src/osp/tasks/execute.h | 14 ++- src/osp/tasks/tasks.h | 12 +- src/osp/tasks/top_execute.cpp | 101 ++++------------ src/osp/tasks/top_execute.h | 8 -- src/osp/tasks/top_session.cpp | 7 +- src/osp/tasks/top_session.h | 47 ++++---- src/osp/tasks/top_utils.h | 8 +- src/osp/tasks/top_worker.h | 21 +--- src/osp/tasks/{task_types.h => worker.h} | 9 +- .../activescenes/identifiers.h | 111 ++++++++---------- .../activescenes/scenarios.cpp | 65 +++++----- src/test_application/activescenes/scenarios.h | 8 ++ .../activescenes/scene_common.cpp | 71 ++++++----- .../activescenes/scene_misc.cpp | 2 +- .../activescenes/scene_newton.cpp | 4 +- .../activescenes/scene_physics.cpp | 4 +- .../activescenes/scene_renderer.cpp | 14 ++- src/test_application/main.cpp | 6 +- src/test_application/testapp.cpp | 5 +- test/tasks/main.cpp | 70 +++++------ 22 files changed, 269 insertions(+), 326 deletions(-) rename src/osp/tasks/{task_types.h => worker.h} (82%) diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 417a0a8b..f3d7bb6b 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -79,15 +79,15 @@ struct TaskBuilderBase } template - std::array create_stateloops() + std::array create_pipelines() { std::array out; m_rTasks.m_pipelineIds.create(out.begin(), out.end()); return out; } - Tasks& m_rTasks; - TaskEdges& m_rEdges; + Tasks & m_rTasks; + TaskEdges & m_rEdges; }; // class TaskBuilderBase diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 6fd9036d..435f0ddc 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -245,7 +245,7 @@ void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe } } -void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept +void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TriggerOut_t dirty) noexcept { LGRN_ASSERT(rExec.tasksQueuedRun.contains(task)); rExec.tasksQueuedRun.erase(task); diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 70128020..68de8832 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -25,12 +25,10 @@ #pragma once #include "tasks.h" +#include "worker.h" #include "../bitvector.h" -#include -#include - #include #include @@ -101,7 +99,13 @@ void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); //}; template -inline void set_dirty(ExecContext &rExec, PipelineDef const pipeline, STAGE_ENUM_T const stage) +inline void set_dirty(ExecContext &rExec, PipelineDef const pipeline, STAGE_ENUM_T const stage) noexcept +{ + rExec.plData[pipeline].triggered |= 1 << int(stage); + rExec.plDirty.set(std::size_t(pipeline)); +} + +inline void set_dirty(ExecContext &rExec, PipelineId const pipeline, StageId const stage) noexcept { rExec.plData[pipeline].triggered |= 1 << int(stage); rExec.plDirty.set(std::size_t(pipeline)); @@ -109,7 +113,7 @@ inline void set_dirty(ExecContext &rExec, PipelineDef const pipeli void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; -void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, FulfillDirty_t dirty) noexcept; +void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TriggerOut_t dirty) noexcept; diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index a5b1ae36..e6d72bd6 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -168,12 +168,6 @@ struct TaskGraph }; -/** - * @brief Bitset returned by tasks to determine which fulfill targets should be marked dirty - */ -using FulfillDirty_t = lgrn::BitView>; - - TaskGraph make_exec_graph(Tasks const& tasks, ArrayView data); inline TaskGraph make_exec_graph(Tasks const& tasks, std::initializer_list data) @@ -271,9 +265,13 @@ struct PipelineDef operator PipelineId() const noexcept { return m_value; } operator std::size_t() const noexcept { return std::size_t(m_value); } - PipelineId& operator=(PipelineId const assign) { m_value = assign; return m_value; } + constexpr PipelineId& operator=(PipelineId const assign) noexcept { m_value = assign; return m_value; } + + constexpr TplPipelineStage tpl(ENUM_T stage) const noexcept { return { m_value, StageId(stage) }; } PipelineId m_value; }; + + } // namespace osp diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index fc38ddf7..f96c50eb 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -46,100 +46,39 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec // Run until there's no tasks left to run while (true) { - if (rExec.m_tasksQueued.empty()) + auto const runTasksLeft = rExec.tasksQueuedRun.size(); + auto const blockedTasksLeft = rExec.tasksQueuedBlocked.size(); + + if (runTasksLeft+blockedTasksLeft == 0) { break; } - TaskId const task = rExec.m_tasksQueued.at(0); - - TopTask &rTopTask = rTaskData[task]; - - std::cout << "running: " << rTopTask.m_debugName << "\n"; - - topDataRefs.clear(); - topDataRefs.reserve(rTopTask.m_dataUsed.size()); - for (TopDataId const dataId : rTopTask.m_dataUsed) + if (runTasksLeft != 0) { - topDataRefs.push_back((dataId != lgrn::id_null()) - ? topData[dataId].as_ref() - : entt::any{}); - } - -// if (rTopTask.m_awareOfDirtyDeps) -// { -// worker.m_dependOnDirty.reset(); + TaskId const task = rExec.tasksQueuedRun.at(0); + TopTask &rTopTask = rTaskData[task]; -// std::size_t index = 0; -// for (TargetId const dependOn : graph.m_taskDependOn[std::size_t(task)]) -// { -// if (rExec.m_targetDirty.test(std::size_t(dependOn))) -// { -// worker.m_dependOnDirty.set(index); -// } -// ++index; -// } -// } + std::cout << "running: " << rTopTask.m_debugName << "\n"; - // Task actually runs here. Results are not yet used for anything. - FulfillDirty_t const status = rTopTask.m_func(worker, topDataRefs); + topDataRefs.clear(); + topDataRefs.reserve(rTopTask.m_dataUsed.size()); + for (TopDataId const dataId : rTopTask.m_dataUsed) + { + topDataRefs.push_back((dataId != lgrn::id_null()) + ? topData[dataId].as_ref() + : entt::any{}); + } - mark_completed_task(tasks, graph, rExec, task, status); + // Task actually runs here. Results are not yet used for anything. + TriggerOut_t const status = rTopTask.m_func(worker, topDataRefs); - enqueue_dirty(tasks, graph, rExec); - for (TaskId const task : rExec.m_tasksQueued) - { - std::cout << "enq: " << rTaskData[task].m_debugName << "\n"; + mark_completed_task(tasks, graph, rExec, task, status); } - } -} -void top_enqueue_quick(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ArrayView enqueue) -{ - for (TargetId const target : enqueue) - { - rExec.m_targetDirty.set(std::size_t(target)); + enqueue_dirty(tasks, graph, rExec); } - - enqueue_dirty(tasks, graph, rExec); } - -void write_dot_graph(std::ostream& rStream, Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData) -{ - rStream << "graph {\n" - << "rankdir=LR;\n" - << "nodesep=0.4;\n" - << "splines=line;\n"; - - // Define targets - rStream << "node [shape=circle,fixedsize=true]; "; - for (std::size_t const target : tasks.m_targetIds.bitview().zeros()) - { - rStream << target << ";"; - } - - rStream << "node [shape=rectangle,fixedsize=false];\n"; - - for (std::size_t const task : tasks.m_taskIds.bitview().zeros()) - { - rStream << "task_" << task << " [label=\"" << rTaskData.at(TaskId(task)).m_debugName << "\"];\n"; - - for (DependOn const dependOn : graph.m_taskDependOn[task]) - { - rStream << std::size_t(dependOn.m_target) << " -- task_" << task << " " - << (dependOn.m_trigger ? "[color=darkblue,penwidth=\"2px\", weight=3,]" : "[color=darkblue,style=dashed]") - << ";\n"; - } - - for (TargetId const fulfill : graph.m_taskFulfill[task]) - { - rStream << "task_" << task << " -- " << std::size_t(fulfill) << " [color=darkred];\n"; - } - } - - rStream << "}"; -} - } // namespace testapp diff --git a/src/osp/tasks/top_execute.h b/src/osp/tasks/top_execute.h index 5c50c729..01089108 100644 --- a/src/osp/tasks/top_execute.h +++ b/src/osp/tasks/top_execute.h @@ -35,13 +35,5 @@ namespace osp void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, WorkerContext worker = {}); -void top_enqueue_quick(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ArrayView enqueue); - -inline void top_enqueue_quick(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, std::initializer_list enqueue) -{ - return top_enqueue_quick(tasks, graph, rExec, Corrade::Containers::arrayView(enqueue)); -} - -void write_dot_graph(std::ostream& rStream, Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData); } // namespace testapp diff --git a/src/osp/tasks/top_session.cpp b/src/osp/tasks/top_session.cpp index de24d278..b1f78a08 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/osp/tasks/top_session.cpp @@ -44,13 +44,12 @@ void top_close_session( ExecContext& rExec, ArrayView sessions) { - // Run cleanup tasks + // Run cleanup pipelines for (Session &rSession : sessions) { - if (TargetId const cleanup = std::exchange(rSession.m_cleanup, lgrn::id_null()); - cleanup != lgrn::id_null()) + if (rSession.m_cleanup.pipeline != lgrn::id_null()) { - rExec.m_targetDirty.set(std::size_t(cleanup)); + set_dirty(rExec, rSession.m_cleanup.pipeline, rSession.m_cleanup.stage); } } enqueue_dirty(rTasks, graph, rExec); diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index f88e78d1..8a64ff0e 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -66,8 +66,7 @@ namespace osp { /** - * @brief A convenient group of TopData, Tasks, and Tags that work together to - * support a certain feature. + * @brief A convenient group of Pipelines that work together to support a certain feature. * * Sessions only store vectors of integer IDs, and don't does not handle * ownership on its own. Close using osp::top_close_session before destruction. @@ -84,52 +83,52 @@ struct Session } template - TGT_STRUCT_T create_targets(BUILDER_T &rBuilder) + TGT_STRUCT_T create_pipelines(BUILDER_T &rBuilder) { - static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); - constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); + static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineId) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineId); std::type_info const& info = typeid(TGT_STRUCT_T); - m_targetStructHash = info.hash_code(); - m_targetStructName = info.name(); + m_structHash = info.hash_code(); + m_structName = info.name(); - m_targets.resize(count); + m_pipelines.resize(count); - rBuilder.m_rTasks.m_targetIds.create(m_targets.begin(), m_targets.end()); + rBuilder.m_rTasks.m_pipelineIds.create(m_pipelines.begin(), m_pipelines.end()); - return reinterpret_cast(*m_targets.data()); + return reinterpret_cast(*m_pipelines.data()); } template - [[nodiscard]] TGT_STRUCT_T get_targets() const + [[nodiscard]] TGT_STRUCT_T get_pipelines() const { - static_assert(sizeof(TGT_STRUCT_T) % sizeof(TargetId) == 0); - constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(TargetId); + static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineId) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineId); std::type_info const& info = typeid(TGT_STRUCT_T); - LGRN_ASSERTMV(m_targetStructHash == info.hash_code() && count == m_targets.size(), - "get_targets must use the same struct given to create_targets", - m_targetStructHash, m_targetStructName, + LGRN_ASSERTMV(m_structHash == info.hash_code() && count == m_pipelines.size(), + "get_pipeline must use the same struct previously given to get_pipelines", + m_structHash, m_structName, info.hash_code(), info.name(), - m_targets.size()); + m_pipelines.size()); - return reinterpret_cast(*m_targets.data()); + return reinterpret_cast(*m_pipelines.data()); } std::vector m_data; - std::vector m_targets; + std::vector m_pipelines; std::vector m_tasks; - TargetId m_cleanup {lgrn::id_null()}; + TplPipelineStage m_cleanup{ lgrn::id_null(), lgrn::id_null() }; - std::size_t m_targetStructHash{0}; - std::string m_targetStructName; + std::size_t m_structHash{0}; + std::string m_structName; }; struct SessionGroup { - std::vector m_sessions; - TaskEdges m_edges; + std::vector m_sessions; + TaskEdges m_edges; }; /** diff --git a/src/osp/tasks/top_utils.h b/src/osp/tasks/top_utils.h index e3fc5367..bd3a46e4 100644 --- a/src/osp/tasks/top_utils.h +++ b/src/osp/tasks/top_utils.h @@ -31,6 +31,8 @@ #include +#include + #include #include #include @@ -128,14 +130,14 @@ struct wrap_args_trait } template - static FulfillDirty_t wrapped_task([[maybe_unused]] WorkerContext ctx, ArrayView topData) noexcept + static TriggerOut_t wrapped_task([[maybe_unused]] WorkerContext ctx, ArrayView topData) noexcept { if constexpr (std::is_void_v) { cast_args(topData, ctx, std::make_index_sequence{}); - return gc_fulfillAll; // All fulfilled targets set dirty + return gc_triggerAll; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { return cast_args(topData, ctx, std::make_index_sequence{}); } diff --git a/src/osp/tasks/top_worker.h b/src/osp/tasks/top_worker.h index 264e9354..c45455b8 100644 --- a/src/osp/tasks/top_worker.h +++ b/src/osp/tasks/top_worker.h @@ -24,6 +24,8 @@ */ #pragma once +#include "worker.h" + #include "../types.h" #include @@ -36,23 +38,6 @@ namespace osp { -/** - * @brief Return value from a task to tell the executor which fulfilled targets are dirty - * - * Bits here limits the max number of fulfill targets per-task to 1*64 - */ -using FulfillDirty_t = lgrn::BitView>; - -/** - * @brief Passed to tasks to signify which targets the task depends on are dirty - * - * Bits here limits the max dependencies per-task to 4*64 - */ -//using DependOnDirty_t = lgrn::BitView>; - -constexpr FulfillDirty_t gc_fulfillAll{{0xFFFFFFFFFFFFFFFFul}}; -constexpr FulfillDirty_t gc_fulfillNone{{0}}; - using TopDataId = uint32_t; using TopDataIds_t = std::initializer_list; @@ -63,6 +48,6 @@ struct WorkerContext //DependOnDirty_t m_dependOnDirty; }; -using TopTaskFunc_t = FulfillDirty_t(*)(WorkerContext, ArrayView) noexcept; +using TopTaskFunc_t = TriggerOut_t(*)(WorkerContext, ArrayView) noexcept; } // namespace osp diff --git a/src/osp/tasks/task_types.h b/src/osp/tasks/worker.h similarity index 82% rename from src/osp/tasks/task_types.h rename to src/osp/tasks/worker.h index af733193..29950c58 100644 --- a/src/osp/tasks/task_types.h +++ b/src/osp/tasks/worker.h @@ -24,11 +24,18 @@ */ #pragma once - +#include namespace osp { +/** + * @brief Bitset returned by tasks to determine which specified (pipeline,stage) to trigger + */ +using TriggerOut_t = std::bitset<64>; + +constexpr TriggerOut_t gc_triggerAll {0xFFFFFFFFFFFFFFFFul}; +constexpr TriggerOut_t gc_triggerNone {0}; } // namespace osp diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 33b766f3..6e188e61 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -29,76 +29,59 @@ namespace testapp { -// Notes: -// * "Delete" tasks often run before "New" tasks, since deletion leaves empty spaces in arrays -// that are best populated with new instances right away. -// * "Delete" task fulfills _del and _mod -// * "New" task depends on _del, fulfills _mod and _new -// - -// Scene sessions - -// Persistent: -// -// DeleteTask --->(_del)---> NewTask --->(_mod)---> UseTask --->(_use) -// | ^ -// +-----------------------------------+ -// - -// Transient: containers filled and cleared right away -// -// PushValuesTask --->(_mod)---> UseValuesTask --->(_use)---> ClearValuesTask --->(_clr) -// +using osp::PipelineDef; + +enum class EStgFlag : uint8_t { Working, Done }; +enum class EStgCont : uint8_t { Delete, New, Modify, Use, Clear }; #define TESTAPP_DATA_SCENE 1, \ idDeltaTimeIn -struct TgtScene +struct PlScene { - osp::TargetId cleanup; - osp::TargetId time; - osp::TargetId sync; - osp::TargetId resyncAll; + PipelineDef cleanup; + PipelineDef time; + //PipelineDef sync; + PipelineDef resyncAll; }; #define TESTAPP_DATA_COMMON_SCENE 6, \ idBasic, idDrawing, idDrawingRes, idActiveEntDel, idDrawEntDel, idNMesh -struct TgtCommonScene +struct PlCommonScene { - osp::TargetId activeEnt_del, activeEnt_new, activeEnt_mod, activeEnt_use; - osp::TargetId delActiveEnt_mod, delActiveEnt_use, delActiveEnt_clr; - osp::TargetId transform_del, transform_new, transform_mod, transform_use; - osp::TargetId hier_del, hier_new, hier_mod, hier_use; - osp::TargetId drawEnt_del, drawEnt_new, drawEnt_mod, drawEnt_use; - osp::TargetId delDrawEnt_mod, delDrawEnt_use, delDrawEnt_clr; - osp::TargetId mesh_del, mesh_new, mesh_mod, mesh_use; - osp::TargetId texture_del, texture_new, texture_mod, texture_use; - osp::TargetId material_del, material_new, material_mod, material_use, material_clr; + PipelineDef activeEnts; + PipelineDef delActiveEnts; + PipelineDef transform; + PipelineDef hierarchy; + PipelineDef drawEnts; + PipelineDef delDrawEnts; + PipelineDef mesh; + PipelineDef texture; + PipelineDef material; }; #define TESTAPP_DATA_PHYSICS 3, \ idPhys, idHierBody, idPhysIn -struct TgtPhysics +struct PlPhysics { - - osp::TargetId physics_del, physics_new, physics_mod, physics_use; + PipelineDef physics; }; #define TESTAPP_DATA_SHAPE_SPAWN 1, \ idSpawner -struct TgtShapeSpawn +struct PlShapeSpawn { - osp::TargetId spawnRequest_mod, spawnRequest_use, spawnRequest_clr; - osp::TargetId spawnedEnts_mod, spawnedEnts_use; // spawnRequest_clr; + PipelineDef spawnRequest; + PipelineDef spawnedEnts; }; #define TESTAPP_DATA_PREFABS 1, \ idPrefabInit -#define OS, tP_TAGS_TESTAPP_PREFABS 7, \ +#define OSP_TAGS_TESTAPP_PREFABS 7, \ tgPrefabMod, tgPrefabReq, tgPrefabClr, \ tgPrefabEntMod, tgPrefabEntReq, \ tgPfParentHierMod, tgPfParentHierReq @@ -170,9 +153,9 @@ struct TgtShapeSpawn #define TESTAPP_DATA_NEWTON 1, \ idNwt -struct TgtNewton +struct PlNewton { - osp::TargetId nwtBody_del, nwtBody_new, nwtBody_mod, nwtBody_use; + PipelineDef nwtBody; }; #define TESTAPP_DATA_NEWTON_FORCES 1, \ @@ -219,49 +202,47 @@ struct TgtNewton #define TESTAPP_DATA_WINDOW_APP 1, \ idUserInput -struct TgtWindowApp +struct PlWindowApp { - osp::TargetId input; - osp::TargetId render; + PipelineDef inputs; + PipelineDef render; }; #define TESTAPP_DATA_MAGNUM 2, \ idActiveApp, idRenderGl -struct TgtMagnum +struct PlMagnum { - osp::TargetId cleanup; - - osp::TargetId meshGL_mod, meshGL_use; - osp::TargetId textureGL_mod, textureGL_use; + PipelineDef cleanup; + PipelineDef meshGL; + PipelineDef textureGL; }; #define TESTAPP_DATA_COMMON_RENDERER 3, \ idScnRender, idGroupFwd, idCamera -struct TgtSceneRenderer +struct PlSceneRenderer { - osp::TargetId fboRender; - osp::TargetId fboRenderDone; - - osp::TargetId scnRender_del, scnRender_new, scnRender_mod, scnRender_use; - osp::TargetId group_mod, group_use; - osp::TargetId groupEnts_del, groupEnts_new, groupEnts_mod, groupEnts_use; - osp::TargetId drawTransform_new, drawTransform_mod, drawTransform_use; - osp::TargetId camera_mod, camera_use; - osp::TargetId entMesh_new, entMesh_mod, entMesh_use; - osp::TargetId entTexture_new, entTexture_mod, entTexture_use; + PipelineDef fboRender; + + PipelineDef scnRender; + PipelineDef group; + PipelineDef groupEnts; + PipelineDef drawTransforms; + PipelineDef camera; + PipelineDef entMesh; + PipelineDef entTexture; }; #define TESTAPP_DATA_CAMERA_CTRL 1, \ idCamCtrl -struct TgtCameraCtrl +struct PlCameraCtrl { - osp::TargetId cameraCtrl_mod, cameraCtrl_use; + PipelineDef camCtrl; }; diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index c4ed321e..b5279c81 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -62,7 +62,7 @@ static constexpr auto sc_matPhong = active::MaterialId(2); static constexpr int sc_materialCount = 4; -static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& scnRenderer, std::vector run = {}) +static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& scnRenderer, std::vector run = {}) { OSP_DECLARE_GET_DATA_IDS(scnRenderer, TESTAPP_DATA_COMMON_RENDERER); OSP_DECLARE_GET_DATA_IDS(rTestApp.m_windowApp, TESTAPP_DATA_WINDOW_APP); @@ -72,28 +72,40 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c auto &rCamera = top_get(rTestApp.m_topData, idCamera); rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); - auto tgScn = scene .get_targets(); - auto tgWin = rTestApp.m_windowApp .get_targets(); + auto tgScn = scene .get_pipelines(); + auto tgWin = rTestApp.m_windowApp .get_pipelines(); - // Run Resync tasks to mark all used gpu resources as dirty - auto aaa = scene.get_targets(); - rTestApp.m_exec.m_targetDirty.set(std::size_t(aaa.resyncAll)); + // Resynchronize scene with new renderer + set_dirty(rTestApp.m_exec, tgScn.resyncAll, EStgFlag::Working); - run.insert(run.end(), {tgScn.sync, tgScn.time, tgWin.input, tgWin.render}); + run.insert(run.end(), { + tgScn.time.tpl(EStgFlag::Working), + tgWin.inputs.tpl(EStgFlag::Working), + tgWin.render.tpl(EStgFlag::Working)}); // run gets copied but who cares lol - rActiveApp.set_on_draw( [&rTestApp, runTagsVec = std::move(run), resync =tgScn.resyncAll] + rActiveApp.set_on_draw( [&rTestApp, run = std::move(run), resync =tgScn.resyncAll] (ActiveApplication& rApp, float delta) { // Magnum Application's main loop is here std::cout << "\n---- START ----\n"; - top_enqueue_quick(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_exec, runTagsVec); + for (auto const [pipeline, stage] : run) + { + set_dirty(rTestApp.m_exec, pipeline, stage); + } + + enqueue_dirty(rTestApp.m_tasks, *rTestApp.m_graph, rTestApp.m_exec); + + for (TaskId const task : rTestApp.m_exec.tasksQueuedRun) + { + std::cout << "run: " << rTestApp.m_taskData[task].m_debugName << "\n"; + } - for (TaskId const task : rTestApp.m_exec.m_tasksQueued) + for (auto const [task, _] : rTestApp.m_exec.tasksQueuedBlocked.each()) { - std::cout << "enq: " << rTestApp.m_taskData[task].m_debugName << "\n"; + std::cout << "blk: " << rTestApp.m_taskData[task].m_debugName << "\n"; } top_run_blocking(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec); @@ -125,7 +137,7 @@ static ScenarioMap_t make_scenarios() SessionGroup &rOut = rTestApp.m_scene; rOut.m_sessions.resize(1); TopDataId const idSceneData = rOut.m_sessions[0].acquire_data<1>(rTestApp.m_topData)[0]; - auto &rResources = osp::top_get(rTestApp.m_topData, rTestApp.m_idResources); + auto &rResources = top_get(rTestApp.m_topData, rTestApp.m_idResources); // enginetest::setup_scene returns an entt::any containing one big // struct containing all the scene data. @@ -134,7 +146,7 @@ static ScenarioMap_t make_scenarios() return [] (TestApp& rTestApp) { TopDataId const idSceneData = rTestApp.m_scene.m_sessions[0].m_data[0]; - auto &rScene = osp::top_get(rTestApp.m_topData, idSceneData); + auto &rScene = top_get(rTestApp.m_topData, idSceneData); OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); OSP_DECLARE_GET_DATA_IDS(rTestApp.m_windowApp, TESTAPP_DATA_WINDOW_APP); @@ -165,23 +177,22 @@ static ScenarioMap_t make_scenarios() // Compose together lots of Sessions scene = setup_scene (builder, rTopData); commonScene = setup_common_scene (builder, rTopData, scene, idResources, defaultPkg); - physics = setup_physics (builder, rTopData, commonScene); - shapeSpawn = setup_shape_spawn (builder, rTopData, commonScene, physics, sc_matVisualizer); + //physics = setup_physics (builder, rTopData, commonScene); + //shapeSpawn = setup_shape_spawn (builder, rTopData, commonScene, physics, sc_matVisualizer); //droppers = setup_droppers (builder, rTopData, commonScene, shapeSpawn); //bounds = setup_bounds (builder, rTopData, commonScene, physics, shapeSpawn); - newton = setup_newton (builder, rTopData, scene, commonScene, physics); - nwtGravSet = setup_newton_factors (builder, rTopData); + //newton = setup_newton (builder, rTopData, scene, commonScene, physics); + //nwtGravSet = setup_newton_factors (builder, rTopData); //nwtGrav = setup_newton_force_accel (builder, rTopData, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); - shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, commonScene, physics, shapeSpawn, newton, nwtGravSet); + //shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, commonScene, physics, shapeSpawn, newton, nwtGravSet); create_materials(rTopData, commonScene, sc_materialCount); add_floor(rTopData, commonScene, shapeSpawn, sc_matVisualizer, idResources, defaultPkg); - rTestApp.m_exec.resize(rTestApp.m_tasks); - auto bbb = commonScene.get_targets(); - rTestApp.m_exec.m_targetDirty.set(std::size_t(commonScene.get_targets().drawEnt_mod)); - rTestApp.m_exec.m_targetDirty.set(std::size_t(shapeSpawn.get_targets().spawnRequest_mod)); + //rTestApp.m_exec.resize(rTestApp.m_tasks); + //rTestApp.m_exec.m_targetDirty.set(std::size_t(commonScene.get_targets().drawEnt_mod)); + //rTestApp.m_exec.m_targetDirty.set(std::size_t(shapeSpawn.get_targets().spawnRequest_mod)); return [] (TestApp& rTestApp) @@ -202,11 +213,11 @@ static ScenarioMap_t make_scenarios() scnRender, cameraCtrl, cameraFree, shVisual, camThrow ] = resize_then_unpack<5>(rTestApp.m_renderer.m_sessions); - scnRender = setup_scene_renderer (builder, rTopData, windowApp, magnum, scene, commonScene, idResources); - cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, scnRender); - cameraFree = setup_camera_free (builder, rTopData, windowApp, scene, cameraCtrl); - shVisual = setup_shader_visualizer (builder, rTopData, magnum, commonScene, scnRender, sc_matVisualizer); - camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); + //scnRender = setup_scene_renderer (builder, rTopData, windowApp, magnum, scene, commonScene, idResources); + //cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, scnRender); + //cameraFree = setup_camera_free (builder, rTopData, windowApp, scene, cameraCtrl); + //shVisual = setup_shader_visualizer (builder, rTopData, magnum, commonScene, scnRender, sc_matVisualizer); + //camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); setup_magnum_draw(rTestApp, scene, scnRender); }; diff --git a/src/test_application/activescenes/scenarios.h b/src/test_application/activescenes/scenarios.h index 8d1cfc83..a849cf79 100644 --- a/src/test_application/activescenes/scenarios.h +++ b/src/test_application/activescenes/scenarios.h @@ -24,6 +24,8 @@ */ #pragma once +#include "identifiers.h" + #include "../testapp.h" // IWYU pragma: begin_exports @@ -35,6 +37,12 @@ namespace testapp { +namespace scenes +{ + using enum EStgCont; + using enum EStgFlag; +} + struct ScenarioOption { std::string_view m_desc; diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index a3315baa..f050c193 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -24,7 +24,6 @@ */ #include "scene_common.h" #include "scenarios.h" -#include "identifiers.h" #include #include @@ -51,7 +50,7 @@ Session setup_scene( top_emplace< float >(topData, idDeltaTimeIn, 1.0f / 60.0f); - out.create_targets(rBuilder); + out.create_pipelines(rBuilder); return out; } @@ -62,12 +61,12 @@ Session setup_common_scene( TopDataId const idResources, PkgId const pkg) { - auto const tgScn = scene.get_targets(); + auto const tgScn = scene.get_pipelines(); auto &rResources = top_get< Resources > (topData, idResources); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_COMMON_SCENE); - auto const tgCS = out.create_targets(rBuilder); + auto const tgCS = out.create_pipelines(rBuilder); /* unused */ top_emplace< ActiveEntVec_t > (topData, idActiveEntDel); /* unused */ top_emplace< DrawEntVec_t > (topData, idDrawEntDel); @@ -78,19 +77,23 @@ Session setup_common_scene( rBuilder.task() .name ("Set materials, meshes, and textures dirty") - .trigger_on ({tgScn.resyncAll}) - .fulfills ({tgCS.texture_mod, tgCS.mesh_mod}) + .run_on ({{tgScn.resyncAll, Working}}) + .sync_with ({{tgCS.texture, Modify}, {tgCS.mesh, Modify}}) + .triggers ({{tgCS.texture, Use}, {tgCS.texture, Clear}, + {tgCS.mesh, Use}, {tgCS.mesh, Clear}, + {tgCS.material, Use}, {tgCS.material, Clear}}) .push_to (out.m_tasks) .args ({ idDrawing }) .func([] (ACtxDrawing& rDrawing) noexcept { SysRender::set_dirty_all(rDrawing); + return gc_triggerAll; }); rBuilder.task() .name ("Delete ActiveEnt IDs") - .trigger_on ({tgCS.delActiveEnt_mod}) - .fulfills ({tgCS.delActiveEnt_use, tgCS.activeEnt_del, tgCS.activeEnt_mod}) + .run_on ({{tgCS.delActiveEnts, Use}}) + .sync_with ({{tgCS.activeEnts, Delete}}) .push_to (out.m_tasks) .args ({ idBasic, idActiveEntDel }) .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -106,8 +109,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete basic components") - .trigger_on ({tgCS.delActiveEnt_mod}) - .fulfills ({tgCS.delActiveEnt_use, tgCS.transform_del, tgCS.transform_mod}) + .run_on ({{tgCS.delActiveEnts, Use}}) + .sync_with ({{tgCS.transform, Delete}}) .push_to (out.m_tasks) .args ({ idBasic, idActiveEntDel }) .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -117,8 +120,9 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEntity of deleted ActiveEnts") - .trigger_on ({tgCS.delActiveEnt_mod}) - .fulfills ({tgCS.delActiveEnt_use, tgCS.delDrawEnt_mod}) + .run_on ({{tgCS.delActiveEnts, Use}}) + .sync_with ({{tgCS.delDrawEnts, Modify}}) + .triggers ({{tgCS.delDrawEnts, Use}, {tgCS.delDrawEnts, Clear}}) .push_to (out.m_tasks) .args ({ idDrawing, idActiveEntDel, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, ActiveEntVec_t const& rActiveEntDel, DrawEntVec_t& rDrawEntDel) noexcept @@ -131,12 +135,15 @@ Session setup_common_scene( rDrawEntDel.push_back(drawEnt); } } + + return rDrawEntDel.empty() ? gc_triggerNone : gc_triggerAll; }); rBuilder.task() .name ("Delete drawing components") - .trigger_on ({tgCS.delDrawEnt_mod}) - .fulfills ({tgCS.delDrawEnt_use, tgCS.mesh_del, tgCS.mesh_mod, tgCS.texture_del, tgCS.texture_mod}) + .run_on ({{tgCS.delDrawEnts, Use}}) + .sync_with ({{tgCS.mesh, Delete}, + {tgCS.texture, Delete}}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept @@ -146,8 +153,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEntity IDs") - .trigger_on ({tgCS.delDrawEnt_mod}) - .fulfills ({tgCS.delDrawEnt_use, tgCS.drawEnt_del, tgCS.drawEnt_mod}) + .run_on ({{tgCS.delDrawEnts, Use}}) + .sync_with ({{tgCS.drawEnts, Delete}}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept @@ -163,8 +170,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEnt from materials") - .trigger_on ({tgCS.delDrawEnt_mod}) - .fulfills ({tgCS.delDrawEnt_use, tgCS.material_del, tgCS.material_mod}) + .run_on ({{tgCS.delDrawEnts, Use}}) + .sync_with ({{tgCS.material, Delete}}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept @@ -179,21 +186,28 @@ Session setup_common_scene( }); rBuilder.task() - .name ("Clear delete vectors once we're done with it") - .trigger_on ({tgCS.delActiveEnt_use, tgCS.delDrawEnt_use}) - .fulfills ({tgCS.delActiveEnt_clr, tgCS.delDrawEnt_clr}) + .name ("Clear ActiveEnt delete vector once we're done with it") + .run_on ({{tgCS.delActiveEnts, Clear}}) .push_to (out.m_tasks) - .args ({ idActiveEntDel, idDrawEntDel }) - .func([] (ActiveEntVec_t& idActiveEntDel, DrawEntVec_t& rDrawEntDel) noexcept + .args ({ idActiveEntDel }) + .func([] (ActiveEntVec_t& idActiveEntDel) noexcept { idActiveEntDel.clear(); + }); + + rBuilder.task() + .name ("Clear DrawEnt delete vector once we're done with it") + .run_on ({{tgCS.delDrawEnts, Clear}}) + .push_to (out.m_tasks) + .args ({ idDrawEntDel }) + .func([] (DrawEntVec_t& rDrawEntDel) noexcept + { rDrawEntDel.clear(); }); rBuilder.task() .name ("Clear material dirty vectors once we're done with it") - .trigger_on ({tgCS.material_use}) - .fulfills ({tgCS.material_clr}) + .run_on ({{tgCS.material, Clear}}) .push_to (out.m_tasks) .args ({ idDrawing }) .func([] (ACtxDrawing& rDrawing) noexcept @@ -206,8 +220,8 @@ Session setup_common_scene( rBuilder.task() .name ("Clean up scene and resource owners") - .trigger_on ({tgScn.cleanup}) - .depends_on ({tgCS.mesh_use, tgCS.texture_use}) + .run_on ({{tgScn.cleanup, Working}}) + .sync_with ({{tgCS.mesh, Clear}, {tgCS.texture, Clear}}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idResources}) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources) noexcept @@ -218,8 +232,7 @@ Session setup_common_scene( rBuilder.task() .name ("Clean up NamedMeshes mesh and texture owners") - .trigger_on ({tgScn.cleanup}) - .fulfills ({}) + .run_on ({{tgScn.cleanup, Working}}) .push_to (out.m_tasks) .args ({ idDrawing, idNMesh }) .func([] (ACtxDrawing& rDrawing, NamedMeshes& rNMesh) noexcept diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index 96d37aed..d13ac31e 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -142,7 +142,7 @@ void add_floor( }); } - +/* Session setup_camera_ctrl( TopTaskBuilder& rBuilder, diff --git a/src/test_application/activescenes/scene_newton.cpp b/src/test_application/activescenes/scene_newton.cpp index b81c0138..43c96add 100644 --- a/src/test_application/activescenes/scene_newton.cpp +++ b/src/test_application/activescenes/scene_newton.cpp @@ -56,6 +56,8 @@ using Corrade::Containers::arrayView; namespace testapp::scenes { +#if 0 + Session setup_newton( TopTaskBuilder& rBuilder, ArrayView const topData, @@ -277,7 +279,7 @@ void compound_collect_recurse( } -#if 0 + Session setup_vehicle_spawn_newton( TopTaskBuilder& rBuilder, diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index 2acc51ee..ab3d8f40 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -48,6 +48,8 @@ using Corrade::Containers::arrayView; namespace testapp::scenes { +#if 0 + Session setup_physics( TopTaskBuilder& rBuilder, ArrayView const topData, @@ -222,7 +224,7 @@ Session setup_shape_spawn( return out; } -#if 0 + Session setup_prefabs( diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 5ffc35ca..24ccd766 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -62,13 +62,15 @@ using Magnum::GL::Mesh; namespace testapp::scenes { + + Session setup_window_app( TopTaskBuilder& rBuilder, ArrayView const topData) { Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_WINDOW_APP); - out.create_targets(rBuilder); + out.create_pipelines(rBuilder); auto &rUserInput = osp::top_emplace(topData, idUserInput, 12); config_controls(rUserInput); @@ -88,8 +90,8 @@ Session setup_magnum( Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_MAGNUM); - auto const tgMgn = out.create_targets(rBuilder); - out.m_cleanup = tgMgn.cleanup; + auto const tgMgn = out.create_pipelines(rBuilder); + out.m_cleanup = tgMgn.cleanup.tpl(Working); // Order-dependent; ActiveApplication construction starts OpenGL context, needed by RenderGL /* unused */ top_emplace(topData, idActiveApp, args, rUserInput); @@ -99,7 +101,7 @@ Session setup_magnum( rBuilder.task() .name ("Clean up Magnum renderer") - .depends_on ({tgMgn.cleanup}) + .run_on ({{tgMgn.cleanup, Working}}) .push_to (out.m_tasks) .args ({ idResources, idRenderGl}) .func([] (Resources& rResources, RenderGL& rRenderGl) noexcept @@ -111,6 +113,8 @@ Session setup_magnum( return out; } + +#if 0 Session setup_scene_renderer( TopTaskBuilder& rBuilder, ArrayView const topData, @@ -316,7 +320,7 @@ Session setup_shader_visualizer( return out; } -#if 0 + Session setup_shader_flat( diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index 7a83d1a4..af395778 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -268,13 +268,9 @@ void start_magnum_async() g_testApp.m_rendererSetup(g_testApp); - g_testApp.m_exec.resize(g_testApp.m_tasks); g_testApp.m_graph.reset(); g_testApp.m_graph.emplace(osp::make_exec_graph(g_testApp.m_tasks, {&g_testApp.m_renderer.m_edges, &g_testApp.m_scene.m_edges})); - std::ofstream dot; - dot.open("tasks.gv"); - osp::write_dot_graph(dot, g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_taskData); - dot.close(); + osp::exec_resize(g_testApp.m_tasks, *g_testApp.m_graph, g_testApp.m_exec); enqueue_dirty(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_exec); top_run_blocking(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_taskData, g_testApp.m_topData, g_testApp.m_exec); diff --git a/src/test_application/testapp.cpp b/src/test_application/testapp.cpp index e044c701..20418614 100644 --- a/src/test_application/testapp.cpp +++ b/src/test_application/testapp.cpp @@ -34,9 +34,10 @@ namespace testapp void close_sessions(TestAppTasks &rTestApp, osp::SessionGroup &rSessions) { + rSessions.m_edges.m_runOn .clear(); + rSessions.m_edges.m_syncWith .clear(); + rSessions.m_edges.m_triggers .clear(); rSessions.m_edges.m_semaphoreEdges .clear(); - rSessions.m_edges.m_targetDependEdges .clear(); - rSessions.m_edges.m_targetFulfillEdges .clear(); if ( rSessions.m_sessions.empty() || ! rTestApp.m_graph.has_value() ) { diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 688219e7..ca8b1b47 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -63,8 +63,8 @@ void randomized_singlethreaded_execute(Tasks const& tasks, TaskGraph const& grap if (runTasksLeft != 0) { - TaskId const randomTask = rExec.tasksQueuedRun.at(rRand() % runTasksLeft); - FulfillDirty_t const status = runTask(randomTask); + TaskId const randomTask = rExec.tasksQueuedRun.at(rRand() % runTasksLeft); + TriggerOut_t const status = runTask(randomTask); mark_completed_task(tasks, graph, rExec, randomTask, status); } @@ -98,7 +98,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) // well-suited for this problem, as these per-thread vectors can all be represented with the // same TargetId. - using BasicTraits_t = BasicBuilderTraits&, int&)>; + using BasicTraits_t = BasicBuilderTraits&, int&)>; using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; @@ -121,31 +121,31 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) builder.task() .run_on ({{pl.vec, Fill}}) .triggers({{pl.vec, Use}, {pl.vec, Clear}}) - .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> FulfillDirty_t + .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t { rOut.push_back(in); - return {{0b11}}; + return osp::gc_triggerAll; }); } // Use vector builder.task() .run_on({{pl.vec, Use}}) - .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> FulfillDirty_t + .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t { int const sum = std::accumulate(rOut.begin(), rOut.end(), 0); EXPECT_EQ(sum, in * sc_pusherTaskCount); ++rChecksRun; - return {{0b0}}; + return osp::gc_triggerNone; }); // Clear vector after use builder.task() .run_on({{pl.vec, Clear}}) - .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> FulfillDirty_t + .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t { rOut.clear(); - return {{0b0}}; + return osp::gc_triggerNone; }); // Step 2: Compile tasks into an execution graph @@ -169,7 +169,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) set_dirty(exec, pl.vec, Fill); enqueue_dirty(tasks, graph, exec); - randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output, &checksRun] (TaskId const task) -> FulfillDirty_t + randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output, &checksRun] (TaskId const task) -> TriggerOut_t { return functions[task](input, output, checksRun); }); @@ -209,7 +209,7 @@ TEST(Tasks, BasicSingleThreadedTriggers) using namespace test_b; using enum Stages; - using BasicTraits_t = BasicBuilderTraits; + using BasicTraits_t = BasicBuilderTraits; using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; @@ -228,58 +228,58 @@ TEST(Tasks, BasicSingleThreadedTriggers) builder.task() .run_on ({{pl.normal, Schedule}}) .triggers ({{pl.normal, Write}, {pl.optional, Write}}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { if (rRand() % 2 == 0) { - return {{0b01}}; + return 0b01; // trigger {pl.normal, Write} } else { rState.optionalFlagExpect = true; - return {{0b11}}; + return 0b11; // trigger {pl.normal, Write} and {pl.optional, Write} } }); builder.task() .run_on ({{pl.normal, Write}}) .triggers ({{pl.normal, Read}, {pl.normal, Clear}}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { rState.normalFlag = true; - return {{0b11}}; + return gc_triggerAll; }); builder.task() .run_on ({{pl.optional, Write}}) .triggers ({{pl.optional, Read}, {pl.optional, Clear}}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { rState.optionalFlag = true; - return {{0b11}}; + return gc_triggerAll; }); builder.task() .run_on ({{pl.normal, Read}}) .sync_with({{pl.optional, Read}}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { EXPECT_TRUE(rState.normalFlag); EXPECT_EQ(rState.optionalFlagExpect, rState.optionalFlag); - return {{0b1}}; + return gc_triggerAll; }); builder.task() .run_on ({{pl.normal, Clear}}) .triggers ({{pl.normal, Schedule}}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> FulfillDirty_t + .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { ++ rState.checks; rState.normalFlag = false; rState.optionalFlagExpect = false; rState.optionalFlag = false; - return {{0b1}}; + return gc_triggerAll; }); TaskGraph const graph = make_exec_graph(tasks, {&edges}); @@ -296,7 +296,7 @@ TEST(Tasks, BasicSingleThreadedTriggers) randomized_singlethreaded_execute( tasks, graph, exec, randGen, sc_taskRuns, - [&functions, &world, &randGen] (TaskId const task) -> FulfillDirty_t + [&functions, &world, &randGen] (TaskId const task) -> TriggerOut_t { return functions[task](world, randGen); }); @@ -341,7 +341,7 @@ TEST(Tasks, BasicSingleThreadedGameWorld) using enum StgSimple; using enum StgRender; - using BasicTraits_t = BasicBuilderTraits; + using BasicTraits_t = BasicBuilderTraits; using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; @@ -361,30 +361,30 @@ TEST(Tasks, BasicSingleThreadedGameWorld) builder.task() .run_on ({{pl.time, Use}}) .sync_with({{pl.forces, Recalc}}) - .func( [] (World& rWorld) -> FulfillDirty_t + .func( [] (World& rWorld) -> TriggerOut_t { rWorld.m_forces += 42 * rWorld.m_deltaTimeIn; - return {{0b01}}; + return gc_triggerNone; }); builder.task() .run_on ({{pl.time, Use}}) .sync_with({{pl.forces, Recalc}}) - .func([] (World& rWorld) -> FulfillDirty_t + .func([] (World& rWorld) -> TriggerOut_t { rWorld.m_forces += 1337 * rWorld.m_deltaTimeIn; - return {{0b01}}; + return gc_triggerNone; }); // Main Physics update builder.task() .run_on ({{pl.time, Use}}) .sync_with({{pl.forces, Use}, {pl.positions, Recalc}}) - .func([] (World& rWorld) -> FulfillDirty_t + .func([] (World& rWorld) -> TriggerOut_t { EXPECT_EQ(rWorld.m_forces, 1337 + 42); rWorld.m_positions += rWorld.m_forces; rWorld.m_forces = 0; - return {{0b01}}; + return gc_triggerNone; }); // Draw things moved by physics update. If 'updWorld' wasn't enqueued, then @@ -392,21 +392,21 @@ TEST(Tasks, BasicSingleThreadedGameWorld) builder.task() .run_on ({{pl.render, Render}}) .sync_with({{pl.positions, Use}}) - .func([] (World& rWorld) -> FulfillDirty_t + .func([] (World& rWorld) -> TriggerOut_t { EXPECT_EQ(rWorld.m_positions, 1337 + 42); rWorld.m_canvas.emplace("Physics Cube"); - return {{0b01}}; + return gc_triggerNone; }); // Draw things unrelated to physics. This is allowed to be the first task // to run builder.task() .run_on ({{pl.render, Render}}) - .func([] (World& rWorld) -> FulfillDirty_t + .func([] (World& rWorld) -> TriggerOut_t { rWorld.m_canvas.emplace("Terrain"); - return {{0b01}}; + return gc_triggerNone; }); TaskGraph const graph = make_exec_graph(tasks, {&edges}); @@ -433,7 +433,7 @@ TEST(Tasks, BasicSingleThreadedGameWorld) randomized_singlethreaded_execute( tasks, graph, exec, randGen, 5, - [&functions, &world] (TaskId const task) -> FulfillDirty_t + [&functions, &world] (TaskId const task) -> TriggerOut_t { return functions[task](world); }); From 51b177620092f08ec5a1116e9ced399ec9c952e6 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 2 Jul 2023 20:55:02 -0700 Subject: [PATCH 20/35] Fix more stuff --- src/osp/Active/opengl/SysRenderGL.cpp | 18 ++-- src/osp/Active/opengl/SysRenderGL.h | 19 +++- src/osp/tasks/builder.h | 29 ++---- src/osp/tasks/execute.cpp | 31 +++++- src/osp/tasks/execute.h | 14 +-- src/osp/tasks/tasks.cpp | 1 - src/osp/tasks/tasks.h | 2 + src/osp/tasks/top_session.cpp | 2 +- src/test_application/ActiveApplication.cpp | 3 + .../activescenes/identifiers.h | 73 +++++++++++--- .../activescenes/scenarios.cpp | 33 ++++--- src/test_application/activescenes/scenarios.h | 2 + .../activescenes/scenarios_enginetest.cpp | 3 +- .../activescenes/scene_common.cpp | 47 +++++---- .../activescenes/scene_misc.cpp | 23 +++-- .../activescenes/scene_physics.cpp | 56 ++++++----- .../activescenes/scene_renderer.cpp | 98 ++++++++++--------- src/test_application/main.cpp | 8 +- test/tasks/main.cpp | 54 +++++----- 19 files changed, 311 insertions(+), 205 deletions(-) diff --git a/src/osp/Active/opengl/SysRenderGL.cpp b/src/osp/Active/opengl/SysRenderGL.cpp index 3588cc68..079e2b73 100644 --- a/src/osp/Active/opengl/SysRenderGL.cpp +++ b/src/osp/Active/opengl/SysRenderGL.cpp @@ -111,14 +111,13 @@ void SysRenderGL::setup_context(RenderGL& rCtxGl) } } -void SysRenderGL::sync_scene_resources( - const ACtxDrawingRes &rCtxDrawRes, - Resources &rResources, - RenderGL &rRenderGl) +void SysRenderGL::compile_resource_textures( + ACtxDrawingRes const& rCtxDrawRes, + Resources& rResources, + RenderGL& rRenderGl) { // TODO: Eventually have dirty flags instead of checking every entry. - // Compile required texture resources for ([[maybe_unused]] auto const & [_, scnOwner] : rCtxDrawRes.m_texToRes) { ResId const texRes = scnOwner.value(); @@ -168,8 +167,15 @@ void SysRenderGL::sync_scene_resources( .setStorage(1, textureFormat(imgData.format()), imgData.size()) .setSubImage(0, {}, imgData); } +} + +void SysRenderGL::compile_resource_meshes( + ACtxDrawingRes const& rCtxDrawRes, + Resources& rResources, + RenderGL& rRenderGl) +{ + // TODO: Eventually have dirty flags instead of checking every entry. - // Compile required mesh resources for ([[maybe_unused]] auto const & [_, scnOwner] : rCtxDrawRes.m_meshToRes) { ResId const meshRes = scnOwner.value(); diff --git a/src/osp/Active/opengl/SysRenderGL.h b/src/osp/Active/opengl/SysRenderGL.h index c71b46b9..ac66e0fb 100644 --- a/src/osp/Active/opengl/SysRenderGL.h +++ b/src/osp/Active/opengl/SysRenderGL.h @@ -136,14 +136,25 @@ class SysRenderGL static void clear_resource_owners(RenderGL& rRenderGl, Resources& rResources); /** - * @brief Compile required meshes and textures resources used by a scene + * @brief Compile GPU-side TexGlIds for textures loaded from a Resource (TexId + ResId) * * @param rCtxDrawRes [in] Resources used by the scene - * @param rResources [ref] Application Resources shared with the scene. - * New resource owners may be created. + * @param rResources [ref] Application Resources shared with the scene. New resource owners may be created. * @param rRenderGl [ref] Renderer state */ - static void sync_scene_resources( + static void compile_resource_textures( + ACtxDrawingRes const& rCtxDrawRes, + Resources& rResources, + RenderGL& rRenderGl); + + /** + * @brief Compile GPU-side MeshGlIds for meshes loaded from a Resource (MeshId + ResId) + * + * @param rCtxDrawRes [in] Resources used by the scene + * @param rResources [ref] Application Resources shared with the scene. New resource owners may be created. + * @param rRenderGl [ref] Renderer state + */ + static void compile_resource_meshes( ACtxDrawingRes const& rCtxDrawRes, Resources& rResources, RenderGL& rRenderGl); diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index f3d7bb6b..acf8ad8b 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -91,17 +91,6 @@ struct TaskBuilderBase }; // class TaskBuilderBase -struct PipelineSpec -{ - template - PipelineSpec(PipelineDef const pipeline, STAGE_ENUM_T const stage) - : m_pipeline{pipeline} - , m_stage{StageId(stage)} - { } - - PipelineId m_pipeline; - StageId m_stage; -}; template struct TaskRefBase @@ -118,43 +107,43 @@ struct TaskRefBase template TASKREF_T& add_edges(std::vector& rContainer, RANGE_T const& add) { - for (PipelineSpec const spec : add) + for (auto const [pipeline, stage] : add) { rContainer.push_back({ .task = m_taskId, - .pipeline = spec.m_pipeline, - .stage = spec.m_stage + .pipeline = pipeline, + .stage = stage }); } return static_cast(*this); } - TASKREF_T& run_on(ArrayView const specs) noexcept + TASKREF_T& run_on(ArrayView const specs) noexcept { return add_edges(m_rBuilder.m_rEdges.m_runOn, specs); } - TASKREF_T& run_on(std::initializer_list specs) noexcept + TASKREF_T& run_on(std::initializer_list specs) noexcept { return add_edges(m_rBuilder.m_rEdges.m_runOn, specs); } - TASKREF_T& sync_with(ArrayView const specs) noexcept + TASKREF_T& sync_with(ArrayView const specs) noexcept { return add_edges(m_rBuilder.m_rEdges.m_syncWith, specs); } - TASKREF_T& sync_with(std::initializer_list specs) noexcept + TASKREF_T& sync_with(std::initializer_list specs) noexcept { return add_edges(m_rBuilder.m_rEdges.m_syncWith, specs); } - TASKREF_T& triggers(ArrayView const specs) noexcept + TASKREF_T& triggers(ArrayView const specs) noexcept { return add_edges(m_rBuilder.m_rEdges.m_triggers, specs); } - TASKREF_T& triggers(std::initializer_list specs) noexcept + TASKREF_T& triggers(std::initializer_list specs) noexcept { return add_edges(m_rBuilder.m_rEdges.m_triggers, specs); } diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 435f0ddc..e93ffe80 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -28,7 +28,7 @@ namespace osp { -void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) +void exec_resize(Tasks const& tasks, ExecContext &rOut) { std::size_t const maxTasks = tasks.m_taskIds.capacity(); std::size_t const maxPipeline = tasks.m_pipelineIds.capacity(); @@ -42,8 +42,22 @@ void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) rOut.plNextStage.resize(maxPipeline); bitvector_resize(rOut.plDirtyNext, maxPipeline); +} + +void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) +{ + exec_resize(tasks, rOut); rOut.anystgReqByTaskCount.resize(graph.anystgToPipeline.size(), 0); + + for (std::size_t pipelineInt : tasks.m_pipelineIds.bitview().zeros()) + { + int const stageCount = fanout_size(graph.pipelineToFirstAnystg, PipelineId(pipelineInt)); + if (stageCount == 0) + { + rOut.plData[PipelineId(pipelineInt)].currentStage = lgrn::id_null(); + } + } } static StageId pipeline_next_stage(TaskGraph const& graph, ExecContext const &exec, ExecPipeline const &execPl, PipelineId const pipeline) noexcept @@ -52,13 +66,20 @@ static StageId pipeline_next_stage(TaskGraph const& graph, ExecContext const &ex if ( execPl.tasksQueuedBlocked != 0 || execPl.tasksQueuedRun != 0 - || execPl.stageReqTaskCount != 0) + || execPl.stageReqTaskCount != 0 + || out == lgrn::id_null()) { return out; // Can't advance. This stage still has (or requires other) tasks to complete + // or pipeline has no stages. } int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); + if (stageCount == 0) + { + return out; + } + while(true) { if (exec.anystgReqByTaskCount[anystg_from(graph, pipeline, out)]) @@ -140,6 +161,12 @@ static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecC { ExecPipeline &rPlExec = rExec.plData[pipeline]; StageId const stage = rExec.plNextStage[pipeline]; + + if (stage == lgrn::id_null()) + { + return; + } + StageBits_t const stageBit = 1 << int(stage); LGRN_ASSERT(rPlExec.tasksQueuedBlocked == 0); diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 68de8832..78b89428 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -92,23 +92,19 @@ struct ExecContext void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); +void exec_resize(Tasks const& tasks, ExecContext &rOut); + //inline bool compare_syncs(SyncWaiting const& lhs, SyncWaiting const& rhs) //{ // return (lhs.m_recipient < rhs.m_recipient) // && (lhs.m_recipient < rhs.m_waitFor); //}; -template -inline void set_dirty(ExecContext &rExec, PipelineDef const pipeline, STAGE_ENUM_T const stage) noexcept -{ - rExec.plData[pipeline].triggered |= 1 << int(stage); - rExec.plDirty.set(std::size_t(pipeline)); -} -inline void set_dirty(ExecContext &rExec, PipelineId const pipeline, StageId const stage) noexcept +inline void set_dirty(ExecContext &rExec, TplPipelineStage tpl) noexcept { - rExec.plData[pipeline].triggered |= 1 << int(stage); - rExec.plDirty.set(std::size_t(pipeline)); + rExec.plData[tpl.pipeline].triggered |= 1 << int(tpl.stage); + rExec.plDirty.set(std::size_t(tpl.pipeline)); } void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index 2abc05d7..568f51de 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -113,7 +113,6 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView // Count TaskRequiresStages and StageRequiresTasks - auto const count_stagereqtask = [&plCounts, &taskCounts, &totalStageReqTasks] (PipelineId const pl, StageId const stg, TaskId const task) { diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index e6d72bd6..b5e9ca11 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -269,6 +269,8 @@ struct PipelineDef constexpr TplPipelineStage tpl(ENUM_T stage) const noexcept { return { m_value, StageId(stage) }; } + constexpr TplPipelineStage operator()(ENUM_T stage) const noexcept { return { m_value, StageId(stage) }; } + PipelineId m_value; }; diff --git a/src/osp/tasks/top_session.cpp b/src/osp/tasks/top_session.cpp index b1f78a08..2938b315 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/osp/tasks/top_session.cpp @@ -49,7 +49,7 @@ void top_close_session( { if (rSession.m_cleanup.pipeline != lgrn::id_null()) { - set_dirty(rExec, rSession.m_cleanup.pipeline, rSession.m_cleanup.stage); + set_dirty(rExec, {rSession.m_cleanup.pipeline, rSession.m_cleanup.stage}); } } enqueue_dirty(rTasks, graph, rExec); diff --git a/src/test_application/ActiveApplication.cpp b/src/test_application/ActiveApplication.cpp index 4f4706e9..937ccd33 100644 --- a/src/test_application/ActiveApplication.cpp +++ b/src/test_application/ActiveApplication.cpp @@ -53,6 +53,9 @@ ActiveApplication::ActiveApplication(const Application::Arguments& arguments, : Application{arguments, Configuration{}.setTitle("OSP-Magnum").setSize({1280, 720})} , m_rUserInput(rUserInput) { + // temporary fixed 60fps. No physics interpolation or anything is implemented yet + setSwapInterval(1); + setMinimalLoopPeriod(16); m_timeline.start(); } diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 6e188e61..397c8366 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -32,7 +32,40 @@ namespace testapp using osp::PipelineDef; enum class EStgFlag : uint8_t { Working, Done }; -enum class EStgCont : uint8_t { Delete, New, Modify, Use, Clear }; + +/** + * @brief Temporary queue / events that are filled, used, then cleared right away + */ +enum class EStgIntr : uint8_t { Resize, Modify_, Use_, Clear }; + +/** + * @brief Continuous Containers, data that persists and is modified over time + * + */ +enum class EStgCont : uint8_t +{ + Delete, + ///< Remove elements from a container or mark them for deletion. This often involves reading + ///< a set of elements to delete. This is run first since it leaves empty spaces for new + ///< elements to fill directly after + + New, + ///< Add new elements. Potentially resize the container to fit more + + Modify, + ///< Modify existing elements + + Use + ///< Container is ready to use +}; + + +enum class EStgRender +{ + Bind, + Draw, + Unbind +}; #define TESTAPP_DATA_SCENE 1, \ idDeltaTimeIn @@ -40,23 +73,37 @@ struct PlScene { PipelineDef cleanup; PipelineDef time; - //PipelineDef sync; PipelineDef resyncAll; + + //PipelineDef sync; }; #define TESTAPP_DATA_COMMON_SCENE 6, \ idBasic, idDrawing, idDrawingRes, idActiveEntDel, idDrawEntDel, idNMesh struct PlCommonScene { - PipelineDef activeEnts; - PipelineDef delActiveEnts; + PipelineDef activeEnt; + PipelineDef activeEntResized; + PipelineDef activeEntDelete; + PipelineDef transform; PipelineDef hierarchy; - PipelineDef drawEnts; - PipelineDef delDrawEnts; + + PipelineDef drawEnt; + PipelineDef drawEntResized; + PipelineDef drawEntDelete; + PipelineDef mesh; PipelineDef texture; + + PipelineDef entTextureDirty; + PipelineDef entMeshDirty; + + PipelineDef meshResDirty; + PipelineDef textureResDirty; + PipelineDef material; + PipelineDef materialDirty; }; @@ -73,8 +120,8 @@ struct PlPhysics idSpawner struct PlShapeSpawn { - PipelineDef spawnRequest; - PipelineDef spawnedEnts; + PipelineDef spawnRequest; + PipelineDef spawnedEnts; }; @@ -205,7 +252,7 @@ struct PlNewton struct PlWindowApp { PipelineDef inputs; - PipelineDef render; + PipelineDef display; }; @@ -215,8 +262,12 @@ struct PlWindowApp struct PlMagnum { PipelineDef cleanup; + PipelineDef meshGL; PipelineDef textureGL; + + PipelineDef entMeshGL; + PipelineDef entTextureGL; }; @@ -225,12 +276,12 @@ struct PlMagnum idScnRender, idGroupFwd, idCamera struct PlSceneRenderer { - PipelineDef fboRender; + PipelineDef fboRender; PipelineDef scnRender; PipelineDef group; PipelineDef groupEnts; - PipelineDef drawTransforms; + PipelineDef drawTransforms; PipelineDef camera; PipelineDef entMesh; PipelineDef entTexture; diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index b5279c81..26cd28bc 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -64,7 +64,7 @@ static constexpr int sc_materialCount = 4; static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& scnRenderer, std::vector run = {}) { - OSP_DECLARE_GET_DATA_IDS(scnRenderer, TESTAPP_DATA_COMMON_RENDERER); + OSP_DECLARE_GET_DATA_IDS(scnRenderer, TESTAPP_DATA_COMMON_RENDERER); OSP_DECLARE_GET_DATA_IDS(rTestApp.m_windowApp, TESTAPP_DATA_WINDOW_APP); OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); @@ -76,15 +76,16 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c auto tgWin = rTestApp.m_windowApp .get_pipelines(); // Resynchronize scene with new renderer - set_dirty(rTestApp.m_exec, tgScn.resyncAll, EStgFlag::Working); + exec_resize(rTestApp.m_tasks, rTestApp.m_exec); + set_dirty(rTestApp.m_exec, tgScn.resyncAll(EStgFlag::Working)); run.insert(run.end(), { tgScn.time.tpl(EStgFlag::Working), tgWin.inputs.tpl(EStgFlag::Working), - tgWin.render.tpl(EStgFlag::Working)}); + tgWin.display.tpl(EStgFlag::Working)}); // run gets copied but who cares lol - rActiveApp.set_on_draw( [&rTestApp, run = std::move(run), resync =tgScn.resyncAll] + rActiveApp.set_on_draw( [&rTestApp, run = std::move(run), resync = tgScn.resyncAll] (ActiveApplication& rApp, float delta) { // Magnum Application's main loop is here @@ -93,7 +94,7 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c for (auto const [pipeline, stage] : run) { - set_dirty(rTestApp.m_exec, pipeline, stage); + set_dirty(rTestApp.m_exec, {pipeline, stage}); } enqueue_dirty(rTestApp.m_tasks, *rTestApp.m_graph, rTestApp.m_exec); @@ -177,8 +178,8 @@ static ScenarioMap_t make_scenarios() // Compose together lots of Sessions scene = setup_scene (builder, rTopData); commonScene = setup_common_scene (builder, rTopData, scene, idResources, defaultPkg); - //physics = setup_physics (builder, rTopData, commonScene); - //shapeSpawn = setup_shape_spawn (builder, rTopData, commonScene, physics, sc_matVisualizer); + physics = setup_physics (builder, rTopData, commonScene); + shapeSpawn = setup_shape_spawn (builder, rTopData, commonScene, physics, sc_matVisualizer); //droppers = setup_droppers (builder, rTopData, commonScene, shapeSpawn); //bounds = setup_bounds (builder, rTopData, commonScene, physics, shapeSpawn); @@ -190,10 +191,14 @@ static ScenarioMap_t make_scenarios() create_materials(rTopData, commonScene, sc_materialCount); add_floor(rTopData, commonScene, shapeSpawn, sc_matVisualizer, idResources, defaultPkg); - //rTestApp.m_exec.resize(rTestApp.m_tasks); - //rTestApp.m_exec.m_targetDirty.set(std::size_t(commonScene.get_targets().drawEnt_mod)); - //rTestApp.m_exec.m_targetDirty.set(std::size_t(shapeSpawn.get_targets().spawnRequest_mod)); + auto const tgCS = commonScene.get_pipelines(); + auto const tgShSp = shapeSpawn.get_pipelines(); + exec_resize(rTestApp.m_tasks, rTestApp.m_exec); + set_dirty(rTestApp.m_exec, tgCS.drawEnt(New)); + set_dirty(rTestApp.m_exec, tgCS.drawEnt(Use)); + set_dirty(rTestApp.m_exec, tgCS.drawEntResized(Working)); + set_dirty(rTestApp.m_exec, tgShSp.spawnRequest(Use_)); return [] (TestApp& rTestApp) { @@ -213,10 +218,10 @@ static ScenarioMap_t make_scenarios() scnRender, cameraCtrl, cameraFree, shVisual, camThrow ] = resize_then_unpack<5>(rTestApp.m_renderer.m_sessions); - //scnRender = setup_scene_renderer (builder, rTopData, windowApp, magnum, scene, commonScene, idResources); - //cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, scnRender); - //cameraFree = setup_camera_free (builder, rTopData, windowApp, scene, cameraCtrl); - //shVisual = setup_shader_visualizer (builder, rTopData, magnum, commonScene, scnRender, sc_matVisualizer); + scnRender = setup_scene_renderer (builder, rTopData, windowApp, magnum, scene, commonScene, idResources); + cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, scnRender); + cameraFree = setup_camera_free (builder, rTopData, windowApp, scene, cameraCtrl); + shVisual = setup_shader_visualizer (builder, rTopData, magnum, commonScene, scnRender, sc_matVisualizer); //camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); setup_magnum_draw(rTestApp, scene, scnRender); diff --git a/src/test_application/activescenes/scenarios.h b/src/test_application/activescenes/scenarios.h index a849cf79..1f3bb884 100644 --- a/src/test_application/activescenes/scenarios.h +++ b/src/test_application/activescenes/scenarios.h @@ -40,7 +40,9 @@ namespace testapp namespace scenes { using enum EStgCont; + using enum EStgIntr; using enum EStgFlag; + using enum EStgRender; } struct ScenarioOption diff --git a/src/test_application/activescenes/scenarios_enginetest.cpp b/src/test_application/activescenes/scenarios_enginetest.cpp index 8bff8518..31d24984 100644 --- a/src/test_application/activescenes/scenarios_enginetest.cpp +++ b/src/test_application/activescenes/scenarios_enginetest.cpp @@ -244,7 +244,8 @@ void sync_test_scene( rRenderer.m_phong); // Load required meshes and textures into OpenGL - SysRenderGL::sync_scene_resources(rScene.m_drawingRes, *rScene.m_pResources, rRenderGl); + SysRenderGL::compile_resource_meshes (rScene.m_drawingRes, *rScene.m_pResources, rRenderGl); + SysRenderGL::compile_resource_textures(rScene.m_drawingRes, *rScene.m_pResources, rRenderGl); // Assign GL meshes to entities with a mesh component SysRenderGL::assign_meshes( diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index f050c193..2eaa930d 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -77,11 +77,9 @@ Session setup_common_scene( rBuilder.task() .name ("Set materials, meshes, and textures dirty") - .run_on ({{tgScn.resyncAll, Working}}) - .sync_with ({{tgCS.texture, Modify}, {tgCS.mesh, Modify}}) - .triggers ({{tgCS.texture, Use}, {tgCS.texture, Clear}, - {tgCS.mesh, Use}, {tgCS.mesh, Clear}, - {tgCS.material, Use}, {tgCS.material, Clear}}) + .run_on ({tgScn.resyncAll(Working)}) + .sync_with ({tgCS.texture(Modify), tgCS.mesh(Modify)}) + .triggers ({tgCS.entTextureDirty(Use_), tgCS.entMeshDirty(Use_), tgCS.materialDirty(Use_), tgCS.textureResDirty(Working), tgCS.meshResDirty(Working)}) .push_to (out.m_tasks) .args ({ idDrawing }) .func([] (ACtxDrawing& rDrawing) noexcept @@ -92,8 +90,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete ActiveEnt IDs") - .run_on ({{tgCS.delActiveEnts, Use}}) - .sync_with ({{tgCS.activeEnts, Delete}}) + .run_on ({tgCS.activeEntDelete(Use_)}) + .sync_with ({tgCS.activeEnt (Delete)}) .push_to (out.m_tasks) .args ({ idBasic, idActiveEntDel }) .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -109,8 +107,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete basic components") - .run_on ({{tgCS.delActiveEnts, Use}}) - .sync_with ({{tgCS.transform, Delete}}) + .run_on ({tgCS.activeEntDelete(Use_)}) + .sync_with ({tgCS.transform (Delete)}) .push_to (out.m_tasks) .args ({ idBasic, idActiveEntDel }) .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -120,9 +118,9 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEntity of deleted ActiveEnts") - .run_on ({{tgCS.delActiveEnts, Use}}) - .sync_with ({{tgCS.delDrawEnts, Modify}}) - .triggers ({{tgCS.delDrawEnts, Use}, {tgCS.delDrawEnts, Clear}}) + .run_on ({tgCS.activeEntDelete(Use_)}) + .sync_with ({tgCS.drawEntDelete (Modify_)}) + .triggers ({tgCS.drawEntDelete (Use_), tgCS.drawEntDelete(Clear)}) .push_to (out.m_tasks) .args ({ idDrawing, idActiveEntDel, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, ActiveEntVec_t const& rActiveEntDel, DrawEntVec_t& rDrawEntDel) noexcept @@ -141,9 +139,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete drawing components") - .run_on ({{tgCS.delDrawEnts, Use}}) - .sync_with ({{tgCS.mesh, Delete}, - {tgCS.texture, Delete}}) + .run_on ({tgCS.drawEntDelete(Use_)}) + .sync_with ({tgCS.mesh(Delete), tgCS.texture(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept @@ -153,8 +150,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEntity IDs") - .run_on ({{tgCS.delDrawEnts, Use}}) - .sync_with ({{tgCS.drawEnts, Delete}}) + .run_on ({tgCS.drawEntDelete(Use_)}) + .sync_with ({tgCS.drawEnt (Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept @@ -170,8 +167,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEnt from materials") - .run_on ({{tgCS.delDrawEnts, Use}}) - .sync_with ({{tgCS.material, Delete}}) + .run_on ({tgCS.drawEntDelete(Use_)}) + .sync_with ({tgCS.material (Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept @@ -187,7 +184,7 @@ Session setup_common_scene( rBuilder.task() .name ("Clear ActiveEnt delete vector once we're done with it") - .run_on ({{tgCS.delActiveEnts, Clear}}) + .run_on ({tgCS.activeEntDelete(Clear)}) .push_to (out.m_tasks) .args ({ idActiveEntDel }) .func([] (ActiveEntVec_t& idActiveEntDel) noexcept @@ -197,7 +194,7 @@ Session setup_common_scene( rBuilder.task() .name ("Clear DrawEnt delete vector once we're done with it") - .run_on ({{tgCS.delDrawEnts, Clear}}) + .run_on ({tgCS.drawEntDelete(Clear)}) .push_to (out.m_tasks) .args ({ idDrawEntDel }) .func([] (DrawEntVec_t& rDrawEntDel) noexcept @@ -207,7 +204,7 @@ Session setup_common_scene( rBuilder.task() .name ("Clear material dirty vectors once we're done with it") - .run_on ({{tgCS.material, Clear}}) + .run_on ({tgCS.materialDirty(Clear)}) .push_to (out.m_tasks) .args ({ idDrawing }) .func([] (ACtxDrawing& rDrawing) noexcept @@ -220,8 +217,8 @@ Session setup_common_scene( rBuilder.task() .name ("Clean up scene and resource owners") - .run_on ({{tgScn.cleanup, Working}}) - .sync_with ({{tgCS.mesh, Clear}, {tgCS.texture, Clear}}) + .run_on ({tgScn.cleanup(Working)}) + .sync_with ({tgCS.mesh(Delete), tgCS.texture(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idResources}) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources) noexcept @@ -232,7 +229,7 @@ Session setup_common_scene( rBuilder.task() .name ("Clean up NamedMeshes mesh and texture owners") - .run_on ({{tgScn.cleanup, Working}}) + .run_on ({tgScn.cleanup(Working)}) .push_to (out.m_tasks) .args ({ idDrawing, idNMesh }) .func([] (ACtxDrawing& rDrawing, NamedMeshes& rNMesh) noexcept diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index d13ac31e..b9db6bc9 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -142,8 +142,6 @@ void add_floor( }); } -/* - Session setup_camera_ctrl( TopTaskBuilder& rBuilder, ArrayView const topData, @@ -153,20 +151,20 @@ Session setup_camera_ctrl( OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); OSP_DECLARE_GET_DATA_IDS(scnRender, TESTAPP_DATA_COMMON_RENDERER); - auto const tgSR = scnRender.get_targets(); + auto const tgSR = scnRender.get_pipelines(); auto &rUserInput = top_get< osp::input::UserInputHandler >(topData, idUserInput); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_CAMERA_CTRL); - auto const tgCmCt = out.create_targets(rBuilder); + auto const tgCmCt = out.create_pipelines(rBuilder); top_emplace< ACtxCameraController > (topData, idCamCtrl, rUserInput); rBuilder.task() .name ("Position Rendering Camera according to Camera Controller") - .trigger_on ({tgCmCt.cameraCtrl_mod}) - .fulfills ({tgCmCt.cameraCtrl_use, tgSR.camera_mod}) + .run_on ({tgCmCt.camCtrl(Use)}) + .sync_with ({tgSR.camera(Modify)}) .push_to (out.m_tasks) .args ({ idCamCtrl, idCamera }) .func([] (ACtxCameraController const& rCamCtrl, Camera &rCamera) noexcept @@ -187,26 +185,31 @@ Session setup_camera_free( OSP_DECLARE_GET_DATA_IDS(scene, TESTAPP_DATA_SCENE); OSP_DECLARE_GET_DATA_IDS(cameraCtrl, TESTAPP_DATA_CAMERA_CTRL); - auto const tgWin = windowApp .get_targets(); - auto const tgCmCt = cameraCtrl .get_targets(); + auto const tgWin = windowApp .get_pipelines(); + auto const tgCmCt = cameraCtrl .get_pipelines(); Session out; rBuilder.task() .name ("Move Camera controller") - .trigger_on ({tgWin.input}) - .fulfills ({tgCmCt.cameraCtrl_mod}) + .run_on ({tgWin.inputs(Working)}) + .sync_with ({tgCmCt.camCtrl(Modify)}) + .triggers ({tgCmCt.camCtrl(Use)}) .push_to (out.m_tasks) .args ({ idCamCtrl, idDeltaTimeIn }) .func([] (ACtxCameraController& rCamCtrl, float const deltaTimeIn) noexcept { SysCameraController::update_view(rCamCtrl, deltaTimeIn); SysCameraController::update_move(rCamCtrl, deltaTimeIn, true); + + return gc_triggerAll; }); return out; } +/* + Session setup_thrower( TopTaskBuilder& rBuilder, ArrayView const topData, diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index ab3d8f40..227bbb73 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -48,7 +48,7 @@ using Corrade::Containers::arrayView; namespace testapp::scenes { -#if 0 + Session setup_physics( TopTaskBuilder& rBuilder, @@ -56,18 +56,18 @@ Session setup_physics( Session const& commonScene) { OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); - auto const tgCS = commonScene.get_targets(); + auto const tgCS = commonScene.get_pipelines(); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_PHYSICS); - auto const tgPhy = out.create_targets(rBuilder); + auto const tgPhy = out.create_pipelines(rBuilder); top_emplace< ACtxPhysics > (topData, idPhys); rBuilder.task() .name ("Delete Physics components") - .trigger_on ({tgCS.delActiveEnt_mod}) - .fulfills ({tgCS.delActiveEnt_use, tgPhy.physics_del, tgPhy.physics_mod}) + .run_on ({tgCS.activeEntDelete(Use_)}) + .sync_with ({tgPhy.physics(Delete)}) .push_to (out.m_tasks) .args ({ idPhys, idActiveEntDel }) .func([] (ACtxPhysics& rPhys, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -87,33 +87,37 @@ Session setup_shape_spawn( { OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); - auto const tgCS = commonScene .get_targets(); - auto const tgPhy = physics .get_targets(); + auto const tgCS = commonScene .get_pipelines(); + auto const tgPhy = physics .get_pipelines(); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_SHAPE_SPAWN); - auto const tgShSp = out.create_targets(rBuilder); + auto const tgShSp = out.create_pipelines(rBuilder); top_emplace< ACtxShapeSpawner > (topData, idSpawner, ACtxShapeSpawner{ .m_materialId = materialId }); rBuilder.task() .name ("Create entities for requested shapes to spawn") - .trigger_on ({tgShSp.spawnRequest_mod}) - .depends_on ({ tgCS.activeEnt_del}) - .fulfills ({tgShSp.spawnRequest_use, tgShSp.spawnedEnts_mod, tgCS.activeEnt_new, tgCS.activeEnt_mod}) + .run_on ({tgShSp.spawnRequest(Use_)}) + .sync_with ({tgCS.activeEnt(New), tgShSp.spawnedEnts(Resize)}) + .triggers ({tgCS.activeEnt(Use), tgShSp.spawnedEnts(Use_)}) .push_to (out.m_tasks) .args ({ idBasic, idSpawner }) .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept { + LGRN_ASSERTM(!rSpawner.m_spawnRequest.empty(), "spawnRequest Use_ shouldn't be triggered if rSpawner.m_spawnRequest is empty!"); + rSpawner.m_ents.resize(rSpawner.m_spawnRequest.size() * 2); rBasic.m_activeIds.create(rSpawner.m_ents.begin(), rSpawner.m_ents.end()); + + return gc_triggerAll; }); rBuilder.task() .name ("Add hierarchy and transform to spawned shapes") - .trigger_on ({tgShSp.spawnRequest_mod}) - .depends_on ({tgShSp.spawnedEnts_mod}) - .fulfills ({tgShSp.spawnRequest_use, tgShSp.spawnedEnts_use, tgCS.transform_new, tgCS.transform_mod, tgCS.hier_new, tgCS.hier_mod}) + .run_on ({tgShSp.spawnRequest(Use_)}) + .sync_with ({tgShSp.spawnedEnts(Use_), tgCS.hierarchy(New), tgCS.transform(New)}) + .triggers ({tgCS.transform(Use)}) .push_to (out.m_tasks) .args ({ idBasic, idSpawner }) .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept @@ -132,13 +136,15 @@ Session setup_shape_spawn( SubtreeBuilder bldRoot = bldScnRoot.add_child(root, 1); bldRoot.add_child(child); } + + return gc_triggerAll; }); rBuilder.task() .name ("Add mesh and material to spawned shapes") - .trigger_on ({tgShSp.spawnRequest_mod}) - .depends_on ({tgShSp.spawnedEnts_mod}) - .fulfills ({tgShSp.spawnRequest_use, tgShSp.spawnedEnts_use, tgCS.mesh_new, tgCS.mesh_mod, tgCS.material_new, tgCS.material_mod, tgCS.drawEnt_new, tgCS.drawEnt_mod}) + .run_on ({tgShSp.spawnRequest(Use_)}) + .sync_with ({tgShSp.spawnedEnts(Use_), tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New)}) + .triggers ({tgCS.drawEntResized(Working)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idSpawner, idNMesh }) .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept @@ -179,9 +185,8 @@ Session setup_shape_spawn( rBuilder.task() .name ("Add physics to spawned shapes") - .trigger_on ({tgShSp.spawnRequest_mod}) - .depends_on ({tgShSp.spawnedEnts_mod}) - .fulfills ({tgShSp.spawnRequest_use, tgShSp.spawnedEnts_use, tgPhy.physics_new, tgPhy.physics_mod}) + .run_on ({tgShSp.spawnRequest(Use_)}) + .sync_with ({tgShSp.spawnedEnts(Use_), tgPhy.physics(New)}) .push_to (out.m_tasks) .args ({ idBasic, idSpawner, idPhys }) .func([] (ACtxBasic const& rBasic, ACtxShapeSpawner& rSpawner, ACtxPhysics& rPhys) noexcept @@ -212,8 +217,7 @@ Session setup_shape_spawn( rBuilder.task() .name ("Clear Shape Spawning vector after use") - .trigger_on ({tgShSp.spawnRequest_use}) - .fulfills ({tgShSp.spawnRequest_clr}) + .run_on ({tgShSp.spawnRequest(Clear)}) .push_to (out.m_tasks) .args ({ idSpawner }) .func([] (ACtxShapeSpawner& rSpawner) noexcept @@ -225,7 +229,7 @@ Session setup_shape_spawn( } - +#if 0 Session setup_prefabs( TopTaskBuilder& rBuilder, @@ -297,7 +301,7 @@ Session setup_prefabs( //SysPrefabInit::init_hierarchy(rPrefabInit, rResources, rBasic.m_hierarchy); })); - prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabReq, tgPrefabEntReq, tgTransformNew}).data( + prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabReq, tgPrefabEntReq, PlransformNew}).data( "Init Prefab transforms", TopDataIds_t{ idPrefabInit, idResources, idBasic }, wrap_args([] (ACtxPrefabInit& rPrefabInit, Resources& rResources, ACtxBasic& rBasic) noexcept @@ -380,11 +384,11 @@ Session setup_bounds( rBuilder.tag(tgBoundsSetReq) .depend_on({tgBoundsSetDel, tgBoundsSetMod}); rBuilder.tag(tgOutOfBoundsMod) .depend_on({tgOutOfBoundsPrv}); - // Bounds are checked are after transforms are final (tgTransformReq) + // Bounds are checked are after transforms are final (PlransformReq) // Out-of-bounds entities are added to rOutOfBounds, and are deleted // at the start of next frame - bounds.task() = rBuilder.task().assign({tgSceneEvt, tgTransformReq, tgBoundsSetReq, tgOutOfBoundsMod}).data( + bounds.task() = rBuilder.task().assign({tgSceneEvt, PlransformReq, tgBoundsSetReq, tgOutOfBoundsMod}).data( "Check for out-of-bounds entities", TopDataIds_t{ idBasic, idBounds, idOutOfBounds}, wrap_args([] (ACtxBasic const& rBasic, EntSet_t const& rBounds, EntVector_t& rOutOfBounds) noexcept diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 24ccd766..4b547eea 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -101,7 +101,7 @@ Session setup_magnum( rBuilder.task() .name ("Clean up Magnum renderer") - .run_on ({{tgMgn.cleanup, Working}}) + .run_on ({tgMgn.cleanup(Working)}) .push_to (out.m_tasks) .args ({ idResources, idRenderGl}) .func([] (Resources& rResources, RenderGL& rRenderGl) noexcept @@ -114,7 +114,6 @@ Session setup_magnum( } -#if 0 Session setup_scene_renderer( TopTaskBuilder& rBuilder, ArrayView const topData, @@ -127,14 +126,14 @@ Session setup_scene_renderer( OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); - auto const tgWin = windowApp .get_targets(); - auto const tgScn = scene .get_targets(); - auto const tgCS = commonScene .get_targets(); - auto const tgMgn = magnum .get_targets(); + auto const tgWin = windowApp .get_pipelines(); + auto const tgScn = scene .get_pipelines(); + auto const tgCS = commonScene .get_pipelines(); + auto const tgMgn = magnum .get_pipelines(); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_COMMON_RENDERER); - auto const tgSR = out.create_targets(rBuilder); + auto const tgSR = out.create_pipelines(rBuilder); top_emplace< ACtxSceneRenderGL > (topData, idScnRender); top_emplace< RenderGroup > (topData, idGroupFwd); @@ -147,9 +146,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Resize Scene Render containers to fit drawable entities") - .trigger_on ({tgCS.drawEnt_mod}) - .fulfills ({tgSR.scnRender_new, tgSR.scnRender_mod, tgSR.drawTransform_new, tgSR.drawTransform_mod, - tgSR.entTexture_new, tgSR.entTexture_mod, tgSR.entMesh_new, tgSR.entMesh_mod}) + .run_on ({tgCS.drawEntResized(Working)}) + .sync_with ({tgSR.scnRender(New), tgSR.drawTransforms(Resize), tgSR.entMesh(New)}) .push_to (out.m_tasks) .args ({ idDrawing, idScnRender}) .func([] (ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept @@ -161,21 +159,31 @@ Session setup_scene_renderer( }); rBuilder.task() - .name ("Synchronize used mesh and texture Resources with GL") - .trigger_on ({tgCS.mesh_mod, tgCS.texture_mod}) - .fulfills ({tgCS.mesh_use, tgCS.texture_use, tgMgn.meshGL_mod, tgMgn.textureGL_mod}) + .name ("Compile Resource Meshes to GL") + .run_on ({tgCS.meshResDirty(Working)}) + .sync_with ({tgCS.mesh(Use), tgMgn.meshGL(New)}) + .push_to (out.m_tasks) + .args ({ idDrawingRes, idResources, idRenderGl }) + .func([] (ACtxDrawingRes const& rDrawingRes, osp::Resources& rResources, RenderGL& rRenderGl) noexcept + { + SysRenderGL::compile_resource_meshes(rDrawingRes, rResources, rRenderGl); + }); + + rBuilder.task() + .name ("Compile Resource Textures to GL") + .run_on ({tgCS.textureResDirty(Working)}) + .sync_with ({tgCS.texture(Use), tgMgn.textureGL(New)}) .push_to (out.m_tasks) .args ({ idDrawingRes, idResources, idRenderGl }) .func([] (ACtxDrawingRes const& rDrawingRes, osp::Resources& rResources, RenderGL& rRenderGl) noexcept { - SysRenderGL::sync_scene_resources(rDrawingRes, rResources, rRenderGl); + SysRenderGL::compile_resource_textures(rDrawingRes, rResources, rRenderGl); }); rBuilder.task() .name ("Assign GL textures to entities with scene textures") - .trigger_on ({tgCS.texture_mod}) - .depends_on ({ tgMgn.textureGL_mod, tgCS.drawEnt_mod, tgSR.entTexture_new}) - .fulfills ({tgCS.texture_use, tgMgn.textureGL_use, tgCS.drawEnt_use, tgSR.entTexture_mod}) + .run_on ({tgCS.entTextureDirty(Use_)}) + .sync_with ({tgCS.texture(Use), tgMgn.textureGL(Use), tgMgn.entTextureGL(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept @@ -185,9 +193,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Assign GL meshes to entities with scene meshes") - .trigger_on ({tgCS.mesh_mod}) - .depends_on ({ tgMgn.meshGL_mod, tgCS.drawEnt_mod, tgSR.entMesh_new}) - .fulfills ({tgCS.mesh_use, tgMgn.meshGL_use, tgCS.drawEnt_use, tgSR.entMesh_mod}) + .run_on ({tgCS.entMeshDirty(Use_)}) + .sync_with ({tgCS.mesh(Use), tgMgn.meshGL(Use), tgMgn.entMeshGL(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept @@ -197,8 +204,9 @@ Session setup_scene_renderer( rBuilder.task() .name ("Bind and display off-screen FBO") - .trigger_on ({tgWin.render}) - .fulfills ({tgSR.fboRender}) + .run_on ({tgWin.display(Working)}) + .sync_with ({tgSR.fboRender(Bind)}) + .triggers ({tgSR.fboRender(Draw), tgSR.fboRender(Unbind)}) .push_to (out.m_tasks) .args ({ idDrawing, idRenderGl, idGroupFwd, idCamera }) .func([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept @@ -215,13 +223,15 @@ Session setup_scene_renderer( // Clear it rFbo.clear( FramebufferClear::Color | FramebufferClear::Depth | FramebufferClear::Stencil); + + return gc_triggerAll; }); rBuilder.task() .name ("Calculate draw transforms") - .trigger_on ({tgSR.fboRender}) - .depends_on ({tgSR.drawTransform_new, tgCS.hier_mod, tgCS.drawEnt_mod, tgCS.transform_mod}) - .fulfills ({tgSR.drawTransform_mod, tgCS.hier_use, tgCS.drawEnt_use, tgCS.transform_use}) + .run_on ({tgCS.transform(Use)}) + .sync_with ({tgCS.hierarchy(Use), tgCS.activeEnt(Use), tgSR.drawTransforms(Modify_)}) + .triggers ({tgSR.drawTransforms(Use_)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idScnRender }) .func([] (ACtxBasic const& rBasic, ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept @@ -235,13 +245,16 @@ Session setup_scene_renderer( rDrawing .m_needDrawTf, rootChildren.begin(), rootChildren.end()); + + return gc_triggerAll; }); rBuilder.task() .name ("Render Entities") - .trigger_on ({tgSR.fboRender}) - .depends_on ({ tgSR.scnRender_mod, tgSR.group_mod, tgSR.groupEnts_mod, tgSR.drawTransform_mod, tgSR.camera_mod, tgSR.entMesh_mod, tgSR.entTexture_mod, tgMgn.meshGL_mod, tgMgn.textureGL_mod, tgCS.drawEnt_mod}) - .fulfills ({tgSR.fboRenderDone, tgSR.scnRender_use, tgSR.group_use, tgSR.groupEnts_use, tgSR.drawTransform_use, tgSR.camera_use, tgSR.entMesh_use, tgSR.entTexture_use, tgMgn.meshGL_use, tgMgn.textureGL_use, tgCS.drawEnt_use}) + .run_on ({tgSR.fboRender(Draw)}) + .sync_with ({tgSR.group(Use), tgSR.groupEnts(Use), tgSR.camera(Use), tgSR.drawTransforms(Use_), tgSR.entMesh(Use), tgSR.entTexture(Use), + tgMgn.entMeshGL(Use), tgMgn.entTextureGL(Use), + tgCS.drawEnt(Use)}) .push_to (out.m_tasks) .args ({ idDrawing, idRenderGl, idGroupFwd, idCamera }) .func([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera, WorkerContext ctx) noexcept @@ -254,8 +267,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Delete entities from render groups") - .trigger_on ({tgCS.delDrawEnt_mod}) - .fulfills ({tgCS.delDrawEnt_use, tgSR.groupEnts_del, tgSR.groupEnts_mod}) + .run_on ({tgCS.drawEntDelete(Use_)}) + .sync_with ({tgSR.groupEnts (Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idGroupFwd, idDrawEntDel }) .func([] (ACtxDrawing const& rDrawing, RenderGroup& rGroup, DrawEntVec_t const& rDrawEntDel) noexcept @@ -280,9 +293,9 @@ Session setup_shader_visualizer( OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(scnRender, TESTAPP_DATA_COMMON_RENDERER); OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); - auto const tgCS = commonScene .get_targets(); - auto const tgSR = scnRender .get_targets(); - auto const tgMgn = magnum .get_targets(); + auto const tgCS = commonScene .get_pipelines(); + auto const tgSR = scnRender .get_pipelines(); + auto const tgMgn = magnum .get_pipelines(); auto &rScnRender = top_get< ACtxSceneRenderGL > (topData, idScnRender); auto &rRenderGl = top_get< RenderGL > (topData, idRenderGl); @@ -306,9 +319,8 @@ Session setup_shader_visualizer( rBuilder.task() .name ("Sync MeshVisualizer shader entities") - .trigger_on ({tgCS.material_mod}) - .depends_on ({ tgSR.groupEnts_mod}) - .fulfills ({tgCS.material_use, tgSR.groupEnts_use, tgSR.group_mod}) + .run_on ({tgCS.materialDirty(Use_)}) + .sync_with ({tgSR.groupEnts(Use), tgSR.group(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idGroupFwd, idDrawShVisual}) .func([] (ACtxDrawing const& rDrawing, RenderGroup& rGroupFwd, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept @@ -320,9 +332,7 @@ Session setup_shader_visualizer( return out; } - - - +#if 0 Session setup_shader_flat( TopTaskBuilder& rBuilder, ArrayView const topData, @@ -356,7 +366,7 @@ Session setup_shader_flat( OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); - shFlat.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, tgTexGlReq, tgGroupFwdMod}).data( + shFlat.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, PlexGlReq, tgGroupFwdMod}).data( "Sync Flat shader entities", TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShFlat}, wrap_args([] (std::vector const& rMatDirty, EntSet_t const& rMatEnts, ACtxDrawing const& rDrawing, ACtxSceneRenderGL const& rScnRender, RenderGroup& rGroupFwd, ACtxDrawFlat& rDrawShFlat) noexcept @@ -403,7 +413,7 @@ Session setup_shader_phong( OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); - shPhong.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, tgTexGlReq, tgGroupFwdMod}).data( + shPhong.task() = rBuilder.task().assign({tgSyncEvt, tgMatReq, tgDrawReq, PlexGlReq, tgGroupFwdMod}).data( "Sync Phong shader entities", TopDataIds_t{ idMatDirty, idMatEnts, idDrawing, idScnRender, idGroupFwd, idDrawShPhong}, wrap_args([] (std::vector const& rMatDirty, EntSet_t const& rMatEnts, ACtxDrawing const& rDrawing, ACtxSceneRenderGL const& rScnRender, RenderGroup& rGroupFwd, ACtxDrawPhong& rDrawShPhong) noexcept @@ -677,17 +687,17 @@ Session setup_uni_test_planets_renderer( { return; } - Vector3 &rCamTgt = rCamCtrl.m_target.value(); + Vector3 &rCamPl = rCamCtrl.m_target.value(); // check origin translation // ADL used for Magnum::Math::sign/floor/abs float const maxDist = 512.0f; - Vector3 const translate = sign(rCamTgt) * floor(abs(rCamTgt) / maxDist) * maxDist; + Vector3 const translate = sign(rCamPl) * floor(abs(rCamPl) / maxDist) * maxDist; if ( ! translate.isZero()) { rCamCtrl.m_transform.translation() -= translate; - rCamTgt -= translate; + rCamPl -= translate; // a bit janky to modify universe stuff directly here, but it works lol Vector3 const rotated = Quaternion(rScnFrame.m_rotation).transformVector(translate); diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index af395778..87595618 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -112,10 +112,10 @@ int main(int argc, char** argv) { Corrade::Utility::Arguments args; args.addSkippedPrefix("magnum", "Magnum options") - .addOption("scene", "none").setHelp("scene", "Set the scene to launch") - .addOption("config").setHelp("config", "path to configuration file to use") - .addBooleanOption("norepl").setHelp("norepl", "don't enter read, evaluate, print, loop.") - .addBooleanOption('v', "verbose").setHelp("verbose", "log verbosely") + .addOption("scene", "none") .setHelp("scene", "Set the scene to launch") + .addOption("config") .setHelp("config", "path to configuration file to use") + .addBooleanOption("norepl") .setHelp("norepl", "don't enter read, evaluate, print, loop.") + .addBooleanOption('v', "verbose") .setHelp("verbose", "log verbosely") .setGlobalHelp("Helptext goes here.") .parse(argc, argv); diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index ca8b1b47..daefd4b3 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -119,8 +119,8 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) for (int i = 0; i < sc_pusherTaskCount; ++i) { builder.task() - .run_on ({{pl.vec, Fill}}) - .triggers({{pl.vec, Use}, {pl.vec, Clear}}) + .run_on ({pl.vec(Fill)}) + .triggers({pl.vec(Use), pl.vec(Clear)}) .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t { rOut.push_back(in); @@ -130,7 +130,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) // Use vector builder.task() - .run_on({{pl.vec, Use}}) + .run_on({pl.vec(Use)}) .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t { int const sum = std::accumulate(rOut.begin(), rOut.end(), 0); @@ -141,7 +141,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) // Clear vector after use builder.task() - .run_on({{pl.vec, Clear}}) + .run_on({pl.vec(Clear)}) .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t { rOut.clear(); @@ -166,7 +166,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) { input = 1 + randGen() % 30; - set_dirty(exec, pl.vec, Fill); + set_dirty(exec, pl.vec(Fill)); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output, &checksRun] (TaskId const task) -> TriggerOut_t @@ -226,8 +226,8 @@ TEST(Tasks, BasicSingleThreadedTriggers) // These tasks run in a loop, triggering each other in a loop builder.task() - .run_on ({{pl.normal, Schedule}}) - .triggers ({{pl.normal, Write}, {pl.optional, Write}}) + .run_on ({pl.normal(Schedule)}) + .triggers ({pl.normal(Write), pl.optional(Write)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { if (rRand() % 2 == 0) @@ -242,8 +242,8 @@ TEST(Tasks, BasicSingleThreadedTriggers) }); builder.task() - .run_on ({{pl.normal, Write}}) - .triggers ({{pl.normal, Read}, {pl.normal, Clear}}) + .run_on ({pl.normal(Write)}) + .triggers ({pl.normal(Read), pl.normal(Clear)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { rState.normalFlag = true; @@ -251,8 +251,8 @@ TEST(Tasks, BasicSingleThreadedTriggers) }); builder.task() - .run_on ({{pl.optional, Write}}) - .triggers ({{pl.optional, Read}, {pl.optional, Clear}}) + .run_on ({pl.optional(Write)}) + .triggers ({pl.optional(Read), pl.optional(Clear)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { rState.optionalFlag = true; @@ -261,8 +261,8 @@ TEST(Tasks, BasicSingleThreadedTriggers) builder.task() - .run_on ({{pl.normal, Read}}) - .sync_with({{pl.optional, Read}}) + .run_on ({pl.normal(Read)}) + .sync_with({pl.optional(Read)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { EXPECT_TRUE(rState.normalFlag); @@ -271,8 +271,8 @@ TEST(Tasks, BasicSingleThreadedTriggers) }); builder.task() - .run_on ({{pl.normal, Clear}}) - .triggers ({{pl.normal, Schedule}}) + .run_on ({pl.normal(Clear)}) + .triggers ({pl.normal(Schedule)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t { ++ rState.checks; @@ -291,7 +291,7 @@ TEST(Tasks, BasicSingleThreadedTriggers) TestState world; - set_dirty(exec, pl.normal, Schedule); + set_dirty(exec, pl.normal(Schedule)); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute( @@ -359,16 +359,16 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Two tasks calculate forces needed by the physics update builder.task() - .run_on ({{pl.time, Use}}) - .sync_with({{pl.forces, Recalc}}) + .run_on ({pl.time(Use)}) + .sync_with({pl.forces(Recalc)}) .func( [] (World& rWorld) -> TriggerOut_t { rWorld.m_forces += 42 * rWorld.m_deltaTimeIn; return gc_triggerNone; }); builder.task() - .run_on ({{pl.time, Use}}) - .sync_with({{pl.forces, Recalc}}) + .run_on ({pl.time(Use)}) + .sync_with({pl.forces(Recalc)}) .func([] (World& rWorld) -> TriggerOut_t { rWorld.m_forces += 1337 * rWorld.m_deltaTimeIn; @@ -377,8 +377,8 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Main Physics update builder.task() - .run_on ({{pl.time, Use}}) - .sync_with({{pl.forces, Use}, {pl.positions, Recalc}}) + .run_on ({pl.time(Use)}) + .sync_with({pl.forces(Use), pl.positions(Recalc)}) .func([] (World& rWorld) -> TriggerOut_t { EXPECT_EQ(rWorld.m_forces, 1337 + 42); @@ -390,8 +390,8 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Draw things moved by physics update. If 'updWorld' wasn't enqueued, then // this will still run, as no 'needPhysics' tasks are incomplete builder.task() - .run_on ({{pl.render, Render}}) - .sync_with({{pl.positions, Use}}) + .run_on ({pl.render(Render)}) + .sync_with({pl.positions(Use)}) .func([] (World& rWorld) -> TriggerOut_t { EXPECT_EQ(rWorld.m_positions, 1337 + 42); @@ -402,7 +402,7 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Draw things unrelated to physics. This is allowed to be the first task // to run builder.task() - .run_on ({{pl.render, Render}}) + .run_on ({pl.render(Render)}) .func([] (World& rWorld) -> TriggerOut_t { rWorld.m_canvas.emplace("Terrain"); @@ -427,8 +427,8 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Enqueue initial tasks // This roughly indicates "Time has changed" and "Render requested" - set_dirty(exec, pl.time, StgSimple::Use); - set_dirty(exec, pl.render, StgRender::Render); + set_dirty(exec, pl.time(StgSimple::Use)); + set_dirty(exec, pl.render(StgRender::Render)); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute( From 244181326325cc752a45754aa6f4ffbc433f5603 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Wed, 5 Jul 2023 14:31:39 -0700 Subject: [PATCH 21/35] Implement Pipeline logging and fix more pipeline issues --- src/osp/tasks/execute.cpp | 72 ++++++++- src/osp/tasks/execute.h | 62 ++++++-- src/osp/tasks/tasks.cpp | 18 ++- src/osp/tasks/tasks.h | 40 ++++- src/osp/tasks/top_execute.cpp | 144 +++++++++++++++++- src/osp/tasks/top_execute.h | 3 + src/osp/tasks/top_session.h | 31 +++- .../activescenes/identifiers.h | 115 ++++++++------ .../activescenes/scenarios.cpp | 28 ++-- .../activescenes/scene_misc.cpp | 15 +- .../activescenes/scene_renderer.cpp | 2 +- 11 files changed, 437 insertions(+), 93 deletions(-) diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index e93ffe80..022153c3 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -35,8 +35,6 @@ void exec_resize(Tasks const& tasks, ExecContext &rOut) rOut.tasksQueuedRun .reserve(maxTasks); rOut.tasksQueuedBlocked.reserve(maxTasks); - bitvector_resize(rOut.tasksTryRun, maxTasks); - rOut.plData.resize(maxPipeline); bitvector_resize(rOut.plDirty, maxPipeline); @@ -60,6 +58,14 @@ void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) } } +static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) +{ + if (rExec.doLogging) + { + rExec.logMsg.push_back(msg); + } +} + static StageId pipeline_next_stage(TaskGraph const& graph, ExecContext const &exec, ExecPipeline const &execPl, PipelineId const pipeline) noexcept { StageId out = execPl.currentStage; @@ -116,6 +122,8 @@ static void apply_pipeline_stage(TaskGraph const& graph, ExecContext &rExec, Pip return; // no change } + exec_log(rExec, ExecContext::StageChange{pipeline, oldStage, newStage}); + rStage = newStage; LGRN_ASSERTV(rPlExec.stageReqTaskCount == 0, rPlExec.stageReqTaskCount); @@ -147,6 +155,7 @@ static void apply_pipeline_stage(TaskGraph const& graph, ExecContext &rExec, Pip -- rBlocked.remainingTaskReqStg; if (rBlocked.remainingTaskReqStg == 0) { + exec_log(rExec, ExecContext::UnblockTask{task}); ExecPipeline &rTaskPlExec = rExec.plData[rBlocked.pipeline]; -- rTaskPlExec.tasksQueuedBlocked; ++ rTaskPlExec.tasksQueuedRun; @@ -161,14 +170,13 @@ static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecC { ExecPipeline &rPlExec = rExec.plData[pipeline]; StageId const stage = rExec.plNextStage[pipeline]; + StageBits_t const stageBit = 1 << int(stage); if (stage == lgrn::id_null()) { return; } - StageBits_t const stageBit = 1 << int(stage); - LGRN_ASSERT(rPlExec.tasksQueuedBlocked == 0); LGRN_ASSERT(rPlExec.tasksQueuedRun == 0); @@ -183,8 +191,33 @@ static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecC AnyStageId const anystg = anystg_from(graph, pipeline, stage); - for (TaskId task : fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)) + auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; + + if (runTasks.size() == 0) + { + // No tasks to run. RunTasks are responsible for setting this pipeline dirty once they're + // all done. If there is none, then this pipeline may get stuck if nothing sets it dirty, + // so set dirty right away. + rExec.plDirtyNext.set(std::size_t(pipeline)); + return; + } + + for (TaskId task : runTasks) { + bool const runsOnManyPipelines = fanout_size(graph.taskToFirstRunstage, task) > 1; + bool const alreadyBlocked = rExec.tasksQueuedBlocked.contains(task); + bool const alreadyRunning = rExec.tasksQueuedRun .contains(task); + bool const alreadyQueued = alreadyBlocked || alreadyRunning; + + LGRN_ASSERTM( ( ! alreadyQueued ) || runsOnManyPipelines, + "Attempt to enqueue a single-stage task that is already running. This is impossible!"); + + if (alreadyBlocked) + { + ++ rPlExec.tasksQueuedBlocked; + continue; + } + // Evaluate Stage-requires-Tasks // * Increment counts for currently running stages that require this task for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) @@ -223,7 +256,9 @@ static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecC } } - if (reqsRemaining != 0) + bool const blocked = reqsRemaining != 0; + + if (blocked) { rExec.tasksQueuedBlocked.emplace(task, BlockedTask{reqsRemaining, pipeline}); ++ rPlExec.tasksQueuedBlocked; @@ -234,6 +269,20 @@ static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecC rExec.tasksQueuedRun.emplace(task); ++ rPlExec.tasksQueuedRun; } + + exec_log(rExec, ExecContext::EnqueueTask{pipeline, stage, task, blocked}); + if (blocked) + { + for (TaskRequiresStage const& req : taskreqstageView) + { + ExecPipeline const &reqPlData = rExec.plData[req.reqPipeline]; + + if (reqPlData.currentStage != req.reqStage) + { + exec_log(rExec, ExecContext::EnqueueTaskReq{req.reqPipeline, req.reqStage}); + } + } + } } } @@ -244,6 +293,8 @@ void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe while (plDirtyOnes.begin() != plDirtyOnes.end()) { + exec_log(rExec, ExecContext::EnqueueCycleStart{}); + // Calculate next stages for (std::size_t const pipelineInt : plDirtyOnes) { @@ -277,6 +328,8 @@ void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext LGRN_ASSERT(rExec.tasksQueuedRun.contains(task)); rExec.tasksQueuedRun.erase(task); + exec_log(rExec, ExecContext::CompleteTask{task}); + auto const try_set_dirty = [&rExec] (ExecPipeline &plExec, PipelineId pipeline) { if ( plExec.tasksQueuedRun == 0 @@ -329,7 +382,12 @@ void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext auto const [pipeline, stage] = triggersView[i]; ExecPipeline &plExec = rExec.plData[pipeline]; - plExec.triggered |= 1 << int(stage); + if ( ! plExec.triggered.test(std::size_t(stage)) ) + { + plExec.triggered.set(std::size_t(stage)); + exec_log(rExec, ExecContext::TriggeredStage{task, pipeline, stage}); + } + try_set_dirty(plExec, pipeline); } } diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 78b89428..abe8dc6d 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -32,6 +32,7 @@ #include #include +#include #include namespace osp @@ -66,18 +67,64 @@ struct ExecContext { KeyedVec plData; BitVector_t plDirty; - BitVector_t plDirtyNext; + entt::basic_sparse_set tasksQueuedRun; entt::basic_storage tasksQueuedBlocked; - BitVector_t tasksTryRun; KeyedVec anystgReqByTaskCount; - // used for updating - + // used for enqueue_dirty as temporary values KeyedVec plNextStage; + BitVector_t plDirtyNext; + + // 'logging' + + struct EnqueueCycleStart { }; + + struct StageChange + { + PipelineId pipeline; + StageId stageOld; + StageId stageNew; + }; + + struct EnqueueTask + { + PipelineId pipeline; + StageId stage; + TaskId task; + bool blocked; + }; + + struct EnqueueTaskReq + { + PipelineId pipeline; + StageId stage; + }; + + struct CompleteTask + { + TaskId task; + }; + + struct TriggeredStage + { + TaskId task; + PipelineId pipeline; + StageId stage; + }; + + struct UnblockTask + { + TaskId task; + }; + + using LogMsg_t = std::variant; + + std::vector logMsg; + bool doLogging{true}; // TODO: Consider multithreading. something something work stealing... // * Allow multiple threads to search for and execute tasks. Atomic access @@ -94,13 +141,6 @@ void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); void exec_resize(Tasks const& tasks, ExecContext &rOut); -//inline bool compare_syncs(SyncWaiting const& lhs, SyncWaiting const& rhs) -//{ -// return (lhs.m_recipient < rhs.m_recipient) -// && (lhs.m_recipient < rhs.m_waitFor); -//}; - - inline void set_dirty(ExecContext &rExec, TplPipelineStage tpl) noexcept { rExec.plData[tpl.pipeline].triggered |= 1 << int(tpl.stage); diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index 568f51de..baf1387d 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -138,8 +138,19 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView for (TaskEdges const* pEdges : data) { + // Each run-on adds... + // * TaskRequiresStage makes task require pipeline to be on stage to be allowed to run + // only if the task runs on multiple pipelines. This means all pipelines need to be + for (auto const [task, pipeline, stage] : pEdges->m_runOn) + { + if (taskCounts[task].runOn > 1) + { + count_taskreqstage(task, pipeline, stage); + } + } + // Each sync-with adds... - // * TaskRequiresStage makes task require pipeline to be on stage + // * TaskRequiresStage makes task require pipeline to be on stage to be allowed to run // * StageRequiresTask for stage to wait for task to complete for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { @@ -303,6 +314,11 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView -- rStageCounts.runTasks; -- rTaskCounts.runOn; -- totalRunTasks; + + if (rTaskCounts.runOn > 1) + { + add_taskreqstage(task, pipeline, stage); + } } for (auto const [task, pipeline, stage] : pEdges->m_syncWith) diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index b5e9ca11..59156366 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -30,10 +30,20 @@ #include #include +#include + #include #include +#include #include +#define OSP_DECLARE_STAGE_NAMES(type, ...) \ + inline osp::ArrayView stage_names([[maybe_unused]] type _) noexcept \ + { \ + static auto const arr = std::array{__VA_ARGS__}; \ + return osp::arrayView( arr.data(), arr.size() ); \ + } + namespace osp { @@ -51,6 +61,17 @@ enum class PipelineId : PipelineInt { }; enum class StageId : StageInt { }; enum class SemaphoreId : SemaphoreInt { }; +struct PipelineInfo +{ + using stage_type_family_t = entt::family; + using stage_type_t = stage_type_family_t::value_type; + + static inline KeyedVec> sm_stageNames; + + std::string_view name; + std::string_view category; + stage_type_t stageType; +}; struct Tasks { @@ -59,6 +80,8 @@ struct Tasks lgrn::IdRegistryStl m_semaIds; KeyedVec m_semaLimits; + + KeyedVec m_pipelineInfo; }; struct TplTaskPipelineStage @@ -262,6 +285,11 @@ constexpr StageId stage_prev(StageId const in, int stageCount) noexcept template struct PipelineDef { + constexpr PipelineDef() = default; + constexpr PipelineDef(std::string_view name) + : m_name{name} + { } + operator PipelineId() const noexcept { return m_value; } operator std::size_t() const noexcept { return std::size_t(m_value); } @@ -271,9 +299,19 @@ struct PipelineDef constexpr TplPipelineStage operator()(ENUM_T stage) const noexcept { return { m_value, StageId(stage) }; } - PipelineId m_value; + std::string_view m_name; + + PipelineInfo::stage_type_t m_type { PipelineInfo::stage_type_family_t::value }; + + PipelineId m_value { lgrn::id_null() }; + + + + }; +using PipelineDefBlank_t = PipelineDef; + } // namespace osp diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index f96c50eb..4d2c0d8f 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -26,13 +26,12 @@ #include "top_worker.h" #include "execute.h" -#include "../logging.h" - #include #include #include +#include #include #include @@ -43,6 +42,13 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec { std::vector topDataRefs; + std::cout << "-- Top Run Blocking called\n"; + + top_write_log(std::cout, tasks, rTaskData, graph, rExec); + rExec.logMsg.clear(); + + top_write_pipeline_states(std::cout, tasks, rTaskData, graph, rExec); + // Run until there's no tasks left to run while (true) { @@ -59,8 +65,6 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec TaskId const task = rExec.tasksQueuedRun.at(0); TopTask &rTopTask = rTaskData[task]; - std::cout << "running: " << rTopTask.m_debugName << "\n"; - topDataRefs.clear(); topDataRefs.reserve(rTopTask.m_dataUsed.size()); for (TopDataId const dataId : rTopTask.m_dataUsed) @@ -74,9 +78,141 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec TriggerOut_t const status = rTopTask.m_func(worker, topDataRefs); mark_completed_task(tasks, graph, rExec, task, status); + top_write_log(std::cout, tasks, rTaskData, graph, rExec); + rExec.logMsg.clear(); } enqueue_dirty(tasks, graph, rExec); + top_write_log(std::cout, tasks, rTaskData, graph, rExec); + rExec.logMsg.clear(); + } +} + +static void write_task_requirements(std::ostream &rStream, Tasks const& tasks, TaskGraph const& graph, ExecContext const& exec, TaskId const task) +{ + auto const taskreqstageView = ArrayView(fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)); + for (TaskRequiresStage const& req : taskreqstageView) + { + ExecPipeline const &reqPlData = exec.plData[req.reqPipeline]; + PipelineInfo const& info = tasks.m_pipelineInfo[req.reqPipeline]; + auto const stageNames = ArrayView{PipelineInfo::sm_stageNames[info.stageType]}; + + if (reqPlData.currentStage != req.reqStage) + { + rStream << "* Requires PL" << std::setw(3) << PipelineInt(req.reqPipeline) << " stage " << stageNames[std::size_t(req.reqStage)] << "\n"; + } + } +} + + +void top_write_pipeline_states(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t const& taskData, TaskGraph const& graph, ExecContext const& exec) +{ + constexpr int nameMinColumns = 48; + + rStream << std::setfill('0'); + + for (PipelineInt plInt : tasks.m_pipelineIds.bitview().zeros()) + { + auto const pl = PipelineId(plInt); + ExecPipeline const &plExec = exec.plData[pl]; + + rStream << "PL" << std::setw(3) << plInt << ": "; + + int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pl); + + PipelineInfo const& info = tasks.m_pipelineInfo[pl]; + + auto const stageNames = ArrayView{PipelineInfo::sm_stageNames[info.stageType]}; + + int charsUsed = 7; // "PL###" + ": " + + for (int stage = 0; stage < stageCount; ++stage) + { + bool const trg = plExec.triggered.test(stage); + bool const sel = int(plExec.currentStage) == stage; + rStream << (sel ? '[' : ' ') + << (trg ? '*' : ' ') + << stageNames[stage] + << (trg ? '*' : ' ') + << (sel ? ']' : ' '); + + charsUsed += 4 + stageNames[stage].size(); + } + + for (; charsUsed < nameMinColumns; ++charsUsed) + { + rStream << ' '; + } + + rStream << "- " << info.name; + + rStream << "\n"; + } + + for (auto const [task, block] : exec.tasksQueuedBlocked.each()) + { + rStream << "Task Blocked: " << "TASK" << TaskInt(task) << " - " << taskData[task].m_debugName << "\n"; + + write_task_requirements(rStream, tasks, graph, exec, task); + } + + // [*run*] - [ not ] - *trig* +} + +void top_write_log(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t const& taskData, TaskGraph const& graph, ExecContext const& exec) +{ + auto const stage_name = [&tasks] (PipelineId pl, StageId stg) -> std::string_view + { + PipelineInfo const& info = tasks.m_pipelineInfo[pl]; + auto const stageNames = ArrayView{PipelineInfo::sm_stageNames[info.stageType]}; + return stageNames[std::size_t(stg)]; + }; + + auto const visitMsg = [&rStream, &tasks, &taskData, &graph, &stage_name] (auto&& msg) + { + using MSG_T = std::decay_t; + if constexpr (std::is_same_v) + { + rStream << "Cycle start\n"; + } + else if constexpr (std::is_same_v) + { + + rStream << "Stage Change PL" << std::setw(3) << PipelineInt(msg.pipeline) << " " + << stage_name(msg.pipeline, msg.stageOld) + << " -> " + << stage_name(msg.pipeline, msg.stageNew) << "\n"; + } + else if constexpr (std::is_same_v) + { + rStream << "Enqueue " << (msg.blocked ? "Blocked" : "Run") + << " on PL" << std::setw(3) << PipelineInt(msg.pipeline) + << " stage " << stage_name(msg.pipeline, msg.stage) + << " TASK" << TaskInt(msg.task) << " - " << taskData[msg.task].m_debugName << "\n"; + } + else if constexpr (std::is_same_v) + { + rStream << "* Requires PL" << std::setw(3) << PipelineInt(msg.pipeline) << " stage " << stage_name(msg.pipeline, msg.stage) << "\n"; + } + else if constexpr (std::is_same_v) + { + rStream << "Complete TASK" << TaskInt(msg.task) << "\n"; + } + else if constexpr (std::is_same_v) + { + rStream << "Triggered from TASK" << TaskInt(msg.task) + << " PL" << std::setw(3) << PipelineInt(msg.pipeline) + << " stage " << stage_name(msg.pipeline, msg.stage) << "\n"; + } + else if constexpr (std::is_same_v) + { + rStream << "* Unblock TASK" << TaskInt(msg.task) << "\n"; + } + }; + + for (ExecContext::LogMsg_t const& msg : exec.logMsg) + { + std::visit(visitMsg, msg); } } diff --git a/src/osp/tasks/top_execute.h b/src/osp/tasks/top_execute.h index 01089108..490ec228 100644 --- a/src/osp/tasks/top_execute.h +++ b/src/osp/tasks/top_execute.h @@ -35,5 +35,8 @@ namespace osp void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, WorkerContext worker = {}); +void top_write_pipeline_states(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t const& taskData, TaskGraph const& graph, ExecContext const& exec); + +void top_write_log(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t const& taskData, TaskGraph const& graph, ExecContext const& exec); } // namespace testapp diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index 8a64ff0e..6a1f661e 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -85,8 +85,8 @@ struct Session template TGT_STRUCT_T create_pipelines(BUILDER_T &rBuilder) { - static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineId) == 0); - constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineId); + static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineDefBlank_t) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineDefBlank_t); std::type_info const& info = typeid(TGT_STRUCT_T); m_structHash = info.hash_code(); @@ -95,15 +95,28 @@ struct Session m_pipelines.resize(count); rBuilder.m_rTasks.m_pipelineIds.create(m_pipelines.begin(), m_pipelines.end()); + rBuilder.m_rTasks.m_pipelineInfo.resize(rBuilder.m_rTasks.m_pipelineIds.capacity()); + + TGT_STRUCT_T out; + auto const members = Corrade::Containers::staticArrayView(reinterpret_cast(&out)); + + for (std::size_t i = 0; i < count; ++i) + { + PipelineId const pl = m_pipelines[i]; + members[i].m_value = pl; + + rBuilder.m_rTasks.m_pipelineInfo[pl].stageType = members[i].m_type; + rBuilder.m_rTasks.m_pipelineInfo[pl].name = members[i].m_name; + } - return reinterpret_cast(*m_pipelines.data()); + return out; } template [[nodiscard]] TGT_STRUCT_T get_pipelines() const { static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineId) == 0); - constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineId); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineDefBlank_t); std::type_info const& info = typeid(TGT_STRUCT_T); LGRN_ASSERTMV(m_structHash == info.hash_code() && count == m_pipelines.size(), @@ -112,7 +125,15 @@ struct Session info.hash_code(), info.name(), m_pipelines.size()); - return reinterpret_cast(*m_pipelines.data()); + TGT_STRUCT_T out; + auto const members = Corrade::Containers::staticArrayView(reinterpret_cast(&out)); + + for (std::size_t i = 0; i < count; ++i) + { + members[i].m_value = m_pipelines[i]; + } + + return out; } std::vector m_data; diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 397c8366..e4341d36 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -24,19 +24,41 @@ */ #pragma once -#include +#include + +#include namespace testapp { -using osp::PipelineDef; +enum class EStgFlag : uint8_t +{ + Done, + Working +}; +OSP_DECLARE_STAGE_NAMES(EStgFlag, "Done", "Working"); + + +enum class EStgEvnt : uint8_t +{ + Waiting, + Fired +}; +OSP_DECLARE_STAGE_NAMES(EStgEvnt, "Waiting", "Fired"); -enum class EStgFlag : uint8_t { Working, Done }; /** * @brief Temporary queue / events that are filled, used, then cleared right away */ -enum class EStgIntr : uint8_t { Resize, Modify_, Use_, Clear }; +enum class EStgIntr : uint8_t +{ + Resize, + Modify_, + Use_, + Clear +}; +OSP_DECLARE_STAGE_NAMES(EStgIntr, "Resize", "Modify", "Use", "Clear"); + /** * @brief Continuous Containers, data that persists and is modified over time @@ -58,6 +80,7 @@ enum class EStgCont : uint8_t Use ///< Container is ready to use }; +OSP_DECLARE_STAGE_NAMES(EStgCont, "Delete", "New", "Modify", "Use"); enum class EStgRender @@ -66,14 +89,20 @@ enum class EStgRender Draw, Unbind }; +OSP_DECLARE_STAGE_NAMES(EStgRender, "Bind", "Draw", "Unbind"); + + +using osp::PipelineDef; + +//----------------------------------------------------------------------------- #define TESTAPP_DATA_SCENE 1, \ idDeltaTimeIn struct PlScene { - PipelineDef cleanup; - PipelineDef time; - PipelineDef resyncAll; + PipelineDef cleanup {"cleanup - Scene cleanup before destruction"}; + PipelineDef time {"time - External Delta Time In"}; + PipelineDef resyncAll {"resyncAll - Resynchronize with "}; //PipelineDef sync; }; @@ -82,28 +111,28 @@ struct PlScene idBasic, idDrawing, idDrawingRes, idActiveEntDel, idDrawEntDel, idNMesh struct PlCommonScene { - PipelineDef activeEnt; - PipelineDef activeEntResized; - PipelineDef activeEntDelete; + PipelineDef activeEnt {"activeEnt"}; + PipelineDef activeEntResized {"activeEntResized"}; + PipelineDef activeEntDelete {"activeEntDelete"}; - PipelineDef transform; - PipelineDef hierarchy; + PipelineDef transform {"transform"}; + PipelineDef hierarchy {"hierarchy"}; - PipelineDef drawEnt; - PipelineDef drawEntResized; - PipelineDef drawEntDelete; + PipelineDef drawEnt {"drawEnt"}; + PipelineDef drawEntResized {"drawEntResized"}; + PipelineDef drawEntDelete {"drawEntDelete"}; - PipelineDef mesh; - PipelineDef texture; + PipelineDef mesh {"mesh"}; + PipelineDef texture {"texture"}; - PipelineDef entTextureDirty; - PipelineDef entMeshDirty; + PipelineDef entTextureDirty {"entTextureDirty"}; + PipelineDef entMeshDirty {"entMeshDirty"}; - PipelineDef meshResDirty; - PipelineDef textureResDirty; + PipelineDef meshResDirty {"meshResDirty"}; + PipelineDef textureResDirty {"textureResDirty"}; - PipelineDef material; - PipelineDef materialDirty; + PipelineDef material {"material"}; + PipelineDef materialDirty {"materialDirty"}; }; @@ -111,7 +140,7 @@ struct PlCommonScene idPhys, idHierBody, idPhysIn struct PlPhysics { - PipelineDef physics; + PipelineDef physics {"physics"}; }; @@ -120,8 +149,8 @@ struct PlPhysics idSpawner struct PlShapeSpawn { - PipelineDef spawnRequest; - PipelineDef spawnedEnts; + PipelineDef spawnRequest {"spawnRequest"}; + PipelineDef spawnedEnts {"spawnedEnts"}; }; @@ -251,8 +280,8 @@ struct PlNewton idUserInput struct PlWindowApp { - PipelineDef inputs; - PipelineDef display; + PipelineDef inputs {"inputs - User inputs in"}; + PipelineDef display {"display - Display new frame"}; }; @@ -261,13 +290,13 @@ struct PlWindowApp idActiveApp, idRenderGl struct PlMagnum { - PipelineDef cleanup; + PipelineDef cleanup {"cleanup Cleanup Magnum"}; - PipelineDef meshGL; - PipelineDef textureGL; + PipelineDef meshGL {"meshGL"}; + PipelineDef textureGL {"textureGL"}; - PipelineDef entMeshGL; - PipelineDef entTextureGL; + PipelineDef entMeshGL {"entMeshGL"}; + PipelineDef entTextureGL {"entTextureGL"}; }; @@ -276,15 +305,15 @@ struct PlMagnum idScnRender, idGroupFwd, idCamera struct PlSceneRenderer { - PipelineDef fboRender; - - PipelineDef scnRender; - PipelineDef group; - PipelineDef groupEnts; - PipelineDef drawTransforms; - PipelineDef camera; - PipelineDef entMesh; - PipelineDef entTexture; + PipelineDef fboRender {"fboRender"}; + + PipelineDef scnRender {"scnRender"}; + PipelineDef group {"group"}; + PipelineDef groupEnts {"groupEnts"}; + PipelineDef drawTransforms {"drawTransforms"}; + PipelineDef camera {"camera"}; + PipelineDef entMesh {"entMesh"}; + PipelineDef entTexture {"entTexture"}; }; @@ -293,7 +322,7 @@ struct PlSceneRenderer idCamCtrl struct PlCameraCtrl { - PipelineDef camCtrl; + PipelineDef camCtrl {"camCtrl"}; }; diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 26cd28bc..4ed1e6b2 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -90,8 +90,6 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c { // Magnum Application's main loop is here - std::cout << "\n---- START ----\n"; - for (auto const [pipeline, stage] : run) { set_dirty(rTestApp.m_exec, {pipeline, stage}); @@ -99,16 +97,6 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c enqueue_dirty(rTestApp.m_tasks, *rTestApp.m_graph, rTestApp.m_exec); - for (TaskId const task : rTestApp.m_exec.tasksQueuedRun) - { - std::cout << "run: " << rTestApp.m_taskData[task].m_debugName << "\n"; - } - - for (auto const [task, _] : rTestApp.m_exec.tasksQueuedBlocked.each()) - { - std::cout << "blk: " << rTestApp.m_taskData[task].m_debugName << "\n"; - } - top_run_blocking(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec); // Enqueued tasks that don't run indicate a deadlock @@ -123,10 +111,24 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c }); } +template +static constexpr void register_stage_enum() +{ + PipelineInfo::stage_type_t const type = PipelineInfo::stage_type_family_t::value; + PipelineInfo::sm_stageNames[type] = stage_names(STAGE_ENUM_T{}); +} + static ScenarioMap_t make_scenarios() { ScenarioMap_t scenarioMap; + PipelineInfo::sm_stageNames.resize(32); + + register_stage_enum(); + register_stage_enum(); + register_stage_enum(); + register_stage_enum(); + auto const add_scenario = [&scenarioMap] (std::string_view name, std::string_view desc, SceneSetupFunc_t run) { scenarioMap.emplace(name, ScenarioOption{desc, run}); @@ -222,7 +224,7 @@ static ScenarioMap_t make_scenarios() cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, scnRender); cameraFree = setup_camera_free (builder, rTopData, windowApp, scene, cameraCtrl); shVisual = setup_shader_visualizer (builder, rTopData, magnum, commonScene, scnRender, sc_matVisualizer); - //camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); + camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); setup_magnum_draw(rTestApp, scene, scnRender); }; diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index b9db6bc9..0424aca8 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -208,7 +208,6 @@ Session setup_camera_free( return out; } -/* Session setup_thrower( TopTaskBuilder& rBuilder, @@ -221,9 +220,9 @@ Session setup_thrower( OSP_DECLARE_GET_DATA_IDS(cameraCtrl, TESTAPP_DATA_CAMERA_CTRL); auto &rCamCtrl = top_get< ACtxCameraController > (topData, idCamCtrl); - auto const tgWin = windowApp .get_targets(); - auto const tgCmCt = cameraCtrl .get_targets(); - auto const tgShSp = shapeSpawn .get_targets(); + auto const tgWin = windowApp .get_pipelines(); + auto const tgCmCt = cameraCtrl.get_pipelines(); + auto const tgShSp = shapeSpawn.get_pipelines(); Session out; auto const [idBtnThrow] = out.acquire_data<1>(topData); @@ -232,9 +231,9 @@ Session setup_thrower( rBuilder.task() .name ("Throw spheres when pressing space") - .trigger_on ({tgWin.input}) - .depends_on ({tgCmCt.cameraCtrl_mod}) - .fulfills ({tgShSp.spawnRequest_mod}) + .run_on ({tgWin.inputs(Working)}) + .sync_with ({tgCmCt.camCtrl(Use), tgShSp.spawnRequest(Modify_)}) + .triggers ({tgShSp.spawnRequest(Use_)}) .push_to (out.m_tasks) .args ({ idCamCtrl, idSpawner, idBtnThrow }) .func([] (ACtxCameraController& rCamCtrl, ACtxShapeSpawner& rSpawner, EButtonControlIndex btnThrow) noexcept @@ -252,7 +251,9 @@ Session setup_thrower( .m_mass = 1.0f, .m_shape = EShape::Sphere }); + return gc_triggerAll; } + return gc_triggerNone; }); return out; diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 4b547eea..3ad6955e 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -230,7 +230,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Calculate draw transforms") .run_on ({tgCS.transform(Use)}) - .sync_with ({tgCS.hierarchy(Use), tgCS.activeEnt(Use), tgSR.drawTransforms(Modify_)}) + .sync_with ({tgCS.hierarchy(Use), tgCS.activeEnt(Use), tgSR.drawTransforms(Modify_), tgCS.drawEnt(Use)}) .triggers ({tgSR.drawTransforms(Use_)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idScnRender }) From 12855830d394e7117c7e37240ecf8897d9bef670 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 9 Jul 2023 01:19:44 -0700 Subject: [PATCH 22/35] Implement pipeline task 'conditions' --- src/osp/tasks/builder.h | 29 +++- src/osp/tasks/execute.cpp | 136 ++++++++++------ src/osp/tasks/execute.h | 34 ++-- src/osp/tasks/tasks.cpp | 37 ++++- src/osp/tasks/tasks.h | 10 +- src/osp/tasks/top_execute.cpp | 49 +++--- src/osp/tasks/top_session.cpp | 2 +- .../activescenes/identifiers.h | 2 +- .../activescenes/scenarios.cpp | 12 +- .../activescenes/scene_renderer.cpp | 5 +- test/tasks/main.cpp | 152 ++++++++++++++++-- 11 files changed, 360 insertions(+), 108 deletions(-) diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index acf8ad8b..8115e6e5 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -68,14 +68,23 @@ struct TaskBuilderBase template TGT_STRUCT_T create_pipelines() { - static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineId) == 0); - constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineId); + static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineDefBlank_t) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineDefBlank_t); - std::array out; + std::array pipelines; - m_rTasks.m_pipelineIds.create(out.begin(), out.end()); + m_rTasks.m_pipelineIds.create(pipelines.begin(), pipelines.end()); + + TGT_STRUCT_T out; + auto const members = Corrade::Containers::staticArrayView(reinterpret_cast(&out)); + + for (std::size_t i = 0; i < count; ++i) + { + PipelineId const pl = pipelines[i]; + members[i].m_value = pl; + } - return reinterpret_cast(*out.data()); + return out; } template @@ -148,6 +157,16 @@ struct TaskRefBase return add_edges(m_rBuilder.m_rEdges.m_triggers, specs); } + TASKREF_T& conditions(ArrayView const specs) noexcept + { + return add_edges(m_rBuilder.m_rEdges.m_conditions, specs); + } + + TASKREF_T& conditions(std::initializer_list specs) noexcept + { + return add_edges(m_rBuilder.m_rEdges.m_conditions, specs); + } + TaskId m_taskId; TASKBUILDER_T & m_rBuilder; diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 022153c3..6fb65c54 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -38,7 +38,6 @@ void exec_resize(Tasks const& tasks, ExecContext &rOut) rOut.plData.resize(maxPipeline); bitvector_resize(rOut.plDirty, maxPipeline); - rOut.plNextStage.resize(maxPipeline); bitvector_resize(rOut.plDirtyNext, maxPipeline); } @@ -66,67 +65,90 @@ static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) } } -static StageId pipeline_next_stage(TaskGraph const& graph, ExecContext const &exec, ExecPipeline const &execPl, PipelineId const pipeline) noexcept +void exec_trigger(ExecContext &rExec, TplPipelineStage tpl) { - StageId out = execPl.currentStage; + StageBits_t &rTriggered = rExec.plData[tpl.pipeline].triggered; - if ( execPl.tasksQueuedBlocked != 0 - || execPl.tasksQueuedRun != 0 - || execPl.stageReqTaskCount != 0 - || out == lgrn::id_null()) + if ( ! rTriggered.test(std::size_t(tpl.stage)) ) { - return out; // Can't advance. This stage still has (or requires other) tasks to complete - // or pipeline has no stages. + rExec.plDirty.set(std::size_t(tpl.pipeline)); + rTriggered.set(std::size_t(tpl.stage)); + + exec_log(rExec, ExecContext::ExternalTrigger{tpl.pipeline, tpl.stage}); + } +} + +static void calculate_next_stage(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept +{ + ExecPipeline &rExecPl = rExec.plData[pipeline]; + + rExecPl.nextStage = rExecPl.currentStage; + rExecPl.stageChanged = false; + + if ( rExecPl.tasksQueuedBlocked != 0 + || rExecPl.tasksQueuedRun != 0 + || rExecPl.stageReqTaskCount != 0 + || rExecPl.currentStage == lgrn::id_null()) + { + // Can't advance. This stage still has (or requires other) tasks to complete + // or pipeline has no stages. + return; } int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); if (stageCount == 0) { - return out; + return; } while(true) { - if (exec.anystgReqByTaskCount[anystg_from(graph, pipeline, out)]) + if (rExec.anystgReqByTaskCount[anystg_from(graph, pipeline, rExecPl.nextStage)] != 0) { - return out; // Can't advance, there are queued tasks that require this stage + // Stop here, there are queued tasks that require this stage + return; } - out = stage_next(out, stageCount); + rExecPl.nextStage = stage_next(rExecPl.nextStage, stageCount); + rExecPl.stageChanged = true; - if (execPl.triggered.test(std::size_t(out))) + if (rExecPl.triggered.test(std::size_t(rExecPl.nextStage))) { - return out; // Stop on this stage to run tasks + // Stop on this stage to run tasks + return; } // No triggered and no waiting tasks means infinite loop. Stage should still move by 1 though. - if ( ( ! execPl.triggered.any() ) - && ( execPl.stageReqTaskCount == 0 ) - && ( execPl.taskReqStageCount == 0 ) ) + if ( ( ! rExecPl.triggered.any() ) + && ( rExecPl.stageReqTaskCount == 0 ) + && ( rExecPl.taskReqStageCount == 0 ) ) { - return out; + return; } } } static void apply_pipeline_stage(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { - ExecPipeline &rPlExec = rExec.plData[pipeline]; - StageId &rStage = rPlExec.currentStage; - StageId const oldStage = rStage; - StageId const newStage = rExec.plNextStage[pipeline]; + ExecPipeline &rExecPl = rExec.plData[pipeline]; + + StageId const oldStage = rExecPl.currentStage; + StageId const newStage = rExecPl.nextStage; - if (oldStage == newStage) + if ( ! rExecPl.stageChanged ) { - return; // no change + return; } + rExecPl.triggerUsed = rExecPl.triggered.test(std::size_t(newStage)); + rExecPl.triggered.reset(std::size_t(newStage)); + exec_log(rExec, ExecContext::StageChange{pipeline, oldStage, newStage}); - rStage = newStage; + rExecPl.currentStage = newStage; - LGRN_ASSERTV(rPlExec.stageReqTaskCount == 0, rPlExec.stageReqTaskCount); + LGRN_ASSERTV(rExecPl.stageReqTaskCount == 0, rExecPl.stageReqTaskCount); // Evaluate Stage-requires-Tasks // * Calculate stageReqTaskCount as the number of required task that are currently queued @@ -142,7 +164,7 @@ static void apply_pipeline_stage(TaskGraph const& graph, ExecContext &rExec, Pip } } - rPlExec.stageReqTaskCount = stageReqTaskCount; + rExecPl.stageReqTaskCount = stageReqTaskCount; // Evaluate Task-requires-Stages // * Increment counts for queued tasks that depend on this stage. This unblocks tasks @@ -168,28 +190,29 @@ static void apply_pipeline_stage(TaskGraph const& graph, ExecContext &rExec, Pip static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { - ExecPipeline &rPlExec = rExec.plData[pipeline]; - StageId const stage = rExec.plNextStage[pipeline]; - StageBits_t const stageBit = 1 << int(stage); + ExecPipeline &rExecPl = rExec.plData[pipeline]; + + if ( ! rExecPl.stageChanged ) + { + return; + } - if (stage == lgrn::id_null()) + if (rExecPl.nextStage == lgrn::id_null()) { return; } - LGRN_ASSERT(rPlExec.tasksQueuedBlocked == 0); - LGRN_ASSERT(rPlExec.tasksQueuedRun == 0); + LGRN_ASSERT(rExecPl.tasksQueuedBlocked == 0); + LGRN_ASSERT(rExecPl.tasksQueuedRun == 0); - if ( (rPlExec.triggered & stageBit) == 0 ) + if ( ! rExecPl.triggerUsed ) { return; // Not triggered } - rPlExec.triggered &= ~stageBit; - // Enqueue all tasks - AnyStageId const anystg = anystg_from(graph, pipeline, stage); + AnyStageId const anystg = anystg_from(graph, pipeline, rExecPl.nextStage); auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; @@ -214,7 +237,7 @@ static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecC if (alreadyBlocked) { - ++ rPlExec.tasksQueuedBlocked; + ++ rExecPl.tasksQueuedBlocked; continue; } @@ -261,16 +284,16 @@ static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecC if (blocked) { rExec.tasksQueuedBlocked.emplace(task, BlockedTask{reqsRemaining, pipeline}); - ++ rPlExec.tasksQueuedBlocked; + ++ rExecPl.tasksQueuedBlocked; } else { // Task can run right away rExec.tasksQueuedRun.emplace(task); - ++ rPlExec.tasksQueuedRun; + ++ rExecPl.tasksQueuedRun; } - exec_log(rExec, ExecContext::EnqueueTask{pipeline, stage, task, blocked}); + exec_log(rExec, ExecContext::EnqueueTask{pipeline, rExecPl.nextStage, task, blocked}); if (blocked) { for (TaskRequiresStage const& req : taskreqstageView) @@ -291,16 +314,16 @@ void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe { auto const plDirtyOnes = rExec.plDirty.ones(); + exec_log(rExec, ExecContext::EnqueueStart{}); + while (plDirtyOnes.begin() != plDirtyOnes.end()) { - exec_log(rExec, ExecContext::EnqueueCycleStart{}); + exec_log(rExec, ExecContext::EnqueueCycle{}); // Calculate next stages for (std::size_t const pipelineInt : plDirtyOnes) { - auto const pipeline = PipelineId(pipelineInt); - ExecPipeline &rPlExec = rExec.plData[pipeline]; - rExec.plNextStage[pipeline] = pipeline_next_stage(graph, rExec, rPlExec, pipeline); + calculate_next_stage(graph, rExec, PipelineId(pipelineInt)); } // Apply next stages @@ -321,9 +344,26 @@ void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe rExec.plDirty .ints().begin()); rExec.plDirtyNext.reset(); } + + exec_log(rExec, ExecContext::EnqueueEnd{}); +} + +bool conditions_satisfied(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task) noexcept +{ + for (auto const [pipeline, stage] : fanout_view(graph.taskToFirstCondition, graph.conditionToPlStage, task)) + { + ExecPipeline &rExecPl = rExec.plData[pipeline]; + if ( rExecPl.currentStage != stage + || ! rExecPl.triggerUsed ) + { + return false; + } + } + + return true; } -void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TriggerOut_t dirty) noexcept +void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TriggerOut_t dirty) noexcept { LGRN_ASSERT(rExec.tasksQueuedRun.contains(task)); rExec.tasksQueuedRun.erase(task); @@ -385,7 +425,7 @@ void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext if ( ! plExec.triggered.test(std::size_t(stage)) ) { plExec.triggered.set(std::size_t(stage)); - exec_log(rExec, ExecContext::TriggeredStage{task, pipeline, stage}); + exec_log(rExec, ExecContext::CompleteTaskTrigger{pipeline, stage}); } try_set_dirty(plExec, pipeline); diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index abe8dc6d..55abcad2 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -42,12 +42,16 @@ struct ExecPipeline { StageBits_t triggered {0}; StageId currentStage {0}; + StageId nextStage {0}; int tasksQueuedRun {0}; int tasksQueuedBlocked {0}; int stageReqTaskCount {0}; int taskReqStageCount {0}; + + bool triggerUsed {false}; + bool stageChanged {false}; }; struct BlockedTask @@ -76,12 +80,13 @@ struct ExecContext // used for enqueue_dirty as temporary values - KeyedVec plNextStage; BitVector_t plDirtyNext; // 'logging' - struct EnqueueCycleStart { }; + struct EnqueueStart { }; + struct EnqueueCycle { }; + struct EnqueueEnd { }; struct StageChange { @@ -104,24 +109,29 @@ struct ExecContext StageId stage; }; - struct CompleteTask + struct UnblockTask { TaskId task; }; - struct TriggeredStage + struct CompleteTask { TaskId task; + }; + + struct CompleteTaskTrigger + { PipelineId pipeline; StageId stage; }; - struct UnblockTask + struct ExternalTrigger { - TaskId task; + PipelineId pipeline; + StageId stage; }; - using LogMsg_t = std::variant; + using LogMsg_t = std::variant; std::vector logMsg; bool doLogging{true}; @@ -141,15 +151,13 @@ void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); void exec_resize(Tasks const& tasks, ExecContext &rOut); -inline void set_dirty(ExecContext &rExec, TplPipelineStage tpl) noexcept -{ - rExec.plData[tpl.pipeline].triggered |= 1 << int(tpl.stage); - rExec.plDirty.set(std::size_t(tpl.pipeline)); -} +void exec_trigger(ExecContext &rExec, TplPipelineStage tpl); void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; -void mark_completed_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TriggerOut_t dirty) noexcept; +bool conditions_satisfied(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId task) noexcept; + +void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId task, TriggerOut_t dirty) noexcept; diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index baf1387d..8df564a1 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -33,7 +33,8 @@ struct TaskCounts { uint16_t requiresStages {0}; uint16_t requiredByStages {0}; - uint16_t triggers {0}; + uint8_t triggers {0}; + uint8_t conditions {0}; uint8_t runOn {0}; }; @@ -91,6 +92,12 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView count_stage(pipeline, stage); } + for (auto const [task, pipeline, stage] : pEdges->m_conditions) + { + taskCounts[task].conditions ++; + count_stage(pipeline, stage); + } + for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { count_stage(pipeline, stage); @@ -158,6 +165,13 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView count_taskreqstage(task, pipeline, stage); } + // Condition implies sync-with + for (auto const [task, pipeline, stage] : pEdges->m_conditions) + { + count_stagereqtask(pipeline, stage, task); + count_taskreqstage(task, pipeline, stage); + } + // Each triggers adds... // * StageRequiresTask on previous stage to wait for task to complete for (auto const [task, pipeline, stage] : pEdges->m_triggers) @@ -179,6 +193,8 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView out.runstageToAnystg .resize(totalRunTasks, lgrn::id_null()); out.taskToFirstTrigger .resize(maxTasks+1, lgrn::id_null()); out.triggerToPlStage .resize(totalTriggers, {lgrn::id_null(), lgrn::id_null()}); + out.taskToFirstCondition .resize(maxTasks+1, lgrn::id_null()); + out.conditionToPlStage .resize(totalTriggers, {lgrn::id_null(), lgrn::id_null()}); out.anystgToFirstStgreqtask .resize(totalStages+1, lgrn::id_null()); out.stgreqtaskData .resize(totalStageReqTasks, {}); out.taskToFirstRevStgreqtask .resize(maxTasks+1, lgrn::id_null()); @@ -219,6 +235,11 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView [&taskCounts, &out] (TaskId task) { return taskCounts[task].triggers; }, [&out] (TaskId, TriggerId) { }); + fanout_partition( + out.taskToFirstCondition, + [&taskCounts, &out] (TaskId task) { return taskCounts[task].conditions; }, + [&out] (TaskId, ConditionId) { }); + fanout_partition( out.anystgToFirstStgreqtask, [&plCounts, &out] (AnyStageId stg) @@ -327,6 +348,17 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView add_taskreqstage(task, pipeline, stage); } + for (auto const [task, pipeline, stage] : pEdges->m_conditions) + { + add_stagereqtask(pipeline, stage, task); + add_taskreqstage(task, pipeline, stage); + + TaskCounts &rTaskCounts = taskCounts[task]; + ConditionId const condition = id_from_count(out.taskToFirstCondition, task, rTaskCounts.conditions); + out.conditionToPlStage[condition] = { pipeline, stage }; + -- rTaskCounts.conditions; + } + for (auto const [task, pipeline, stage] : pEdges->m_triggers) { add_stagereqtask(pipeline, stage_prev(stage, plCounts[pipeline].stages), task); @@ -362,7 +394,8 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView { if ( taskCount.requiredByStages != 0 || taskCount.requiredByStages != 0 - || taskCount.runOn != 0) + || taskCount.runOn != 0 + || taskCount.conditions != 0) { return false; } diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 59156366..4c038496 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -97,6 +97,7 @@ struct TplPipelineStage StageId stage; }; + struct TplTaskSemaphore { TaskId task; @@ -108,6 +109,7 @@ struct TaskEdges std::vector m_runOn; std::vector m_syncWith; std::vector m_triggers; + std::vector m_conditions; std::vector m_semaphoreEdges; }; @@ -116,6 +118,7 @@ enum class AnyStageId : uint32_t { }; enum class RunTaskId : uint32_t { }; enum class RunStageId : uint32_t { }; enum class TriggerId : uint32_t { }; +enum class ConditionId : uint32_t { }; enum class StageReqTaskId : uint32_t { }; enum class ReverseStageReqTaskId : uint32_t { }; @@ -161,11 +164,16 @@ struct TaskGraph KeyedVec taskToFirstRunstage; KeyedVec runstageToAnystg; - // Tasks trigger stages of pipelines + // Tasks trigger many stages of pipelines // TaskId --> TriggerId --> many TplPipelineStage KeyedVec taskToFirstTrigger; KeyedVec triggerToPlStage; + // Tasks can have many conditions + // TaskId --> ConditionId --> many TplPipelineStage + KeyedVec taskToFirstCondition; + KeyedVec conditionToPlStage; + // Each stage has multiple entrance requirements. // AnyStageId <--> many StageEnterReqId KeyedVec anystgToFirstStgreqtask; diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index 4d2c0d8f..6b517aa5 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -75,9 +75,9 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec } // Task actually runs here. Results are not yet used for anything. - TriggerOut_t const status = rTopTask.m_func(worker, topDataRefs); + TriggerOut_t const status = (rTopTask.m_func != nullptr) ? rTopTask.m_func(worker, topDataRefs) : 0; - mark_completed_task(tasks, graph, rExec, task, status); + complete_task(tasks, graph, rExec, task, status); top_write_log(std::cout, tasks, rTaskData, graph, rExec); rExec.logMsg.clear(); } @@ -109,14 +109,12 @@ void top_write_pipeline_states(std::ostream &rStream, Tasks const& tasks, TopTas { constexpr int nameMinColumns = 48; - rStream << std::setfill('0'); - for (PipelineInt plInt : tasks.m_pipelineIds.bitview().zeros()) { auto const pl = PipelineId(plInt); ExecPipeline const &plExec = exec.plData[pl]; - rStream << "PL" << std::setw(3) << plInt << ": "; + rStream << "PL" << std::setw(3) << std::left << plInt << ": "; int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pl); @@ -171,42 +169,53 @@ void top_write_log(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t c auto const visitMsg = [&rStream, &tasks, &taskData, &graph, &stage_name] (auto&& msg) { using MSG_T = std::decay_t; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + rStream << "EnqueueStart\n"; + } + else if constexpr (std::is_same_v) + { + rStream << "EnqueueCycle\n"; + } + else if constexpr (std::is_same_v) { - rStream << "Cycle start\n"; + rStream << "EnqueueEnd\n"; } else if constexpr (std::is_same_v) { - - rStream << "Stage Change PL" << std::setw(3) << PipelineInt(msg.pipeline) << " " + rStream << " StageChange PL" << std::setw(3) << PipelineInt(msg.pipeline) << "(" << stage_name(msg.pipeline, msg.stageOld) << " -> " - << stage_name(msg.pipeline, msg.stageNew) << "\n"; + << stage_name(msg.pipeline, msg.stageNew) << ")\n"; } else if constexpr (std::is_same_v) { - rStream << "Enqueue " << (msg.blocked ? "Blocked" : "Run") + rStream << " Enqueue " << (msg.blocked ? "Blocked" : "Run") << " on PL" << std::setw(3) << PipelineInt(msg.pipeline) - << " stage " << stage_name(msg.pipeline, msg.stage) + << "(" << stage_name(msg.pipeline, msg.stage) << ")" << " TASK" << TaskInt(msg.task) << " - " << taskData[msg.task].m_debugName << "\n"; } else if constexpr (std::is_same_v) { - rStream << "* Requires PL" << std::setw(3) << PipelineInt(msg.pipeline) << " stage " << stage_name(msg.pipeline, msg.stage) << "\n"; + rStream << " * Requires PL" << std::setw(3) << PipelineInt(msg.pipeline) << "(" << stage_name(msg.pipeline, msg.stage) << ")\n"; + } + else if constexpr (std::is_same_v) + { + rStream << " * Unblock TASK" << TaskInt(msg.task) << "\n"; } else if constexpr (std::is_same_v) { - rStream << "Complete TASK" << TaskInt(msg.task) << "\n"; + rStream << "Complete TASK" << TaskInt(msg.task) << " - " << taskData[msg.task].m_debugName << "\n"; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - rStream << "Triggered from TASK" << TaskInt(msg.task) - << " PL" << std::setw(3) << PipelineInt(msg.pipeline) - << " stage " << stage_name(msg.pipeline, msg.stage) << "\n"; + rStream << "* Trigger PL" << std::setw(3) << PipelineInt(msg.pipeline) + << "(" << stage_name(msg.pipeline, msg.stage) << ")\n"; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - rStream << "* Unblock TASK" << TaskInt(msg.task) << "\n"; + rStream << "ExternalTrigger PL" << std::setw(3) << PipelineInt(msg.pipeline) + << "(" << stage_name(msg.pipeline, msg.stage) << ")\n"; } }; diff --git a/src/osp/tasks/top_session.cpp b/src/osp/tasks/top_session.cpp index 2938b315..1d624ae3 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/osp/tasks/top_session.cpp @@ -49,7 +49,7 @@ void top_close_session( { if (rSession.m_cleanup.pipeline != lgrn::id_null()) { - set_dirty(rExec, {rSession.m_cleanup.pipeline, rSession.m_cleanup.stage}); + exec_trigger(rExec, {rSession.m_cleanup.pipeline, rSession.m_cleanup.stage}); } } enqueue_dirty(rTasks, graph, rExec); diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index e4341d36..97472ede 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -72,7 +72,7 @@ enum class EStgCont : uint8_t ///< elements to fill directly after New, - ///< Add new elements. Potentially resize the container to fit more + ///< Add new elements. Potentially resize the container to fit more elements Modify, ///< Modify existing elements diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 4ed1e6b2..45d53b4e 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -77,7 +77,7 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c // Resynchronize scene with new renderer exec_resize(rTestApp.m_tasks, rTestApp.m_exec); - set_dirty(rTestApp.m_exec, tgScn.resyncAll(EStgFlag::Working)); + exec_trigger(rTestApp.m_exec, tgScn.resyncAll(EStgFlag::Working)); run.insert(run.end(), { tgScn.time.tpl(EStgFlag::Working), @@ -92,7 +92,7 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c for (auto const [pipeline, stage] : run) { - set_dirty(rTestApp.m_exec, {pipeline, stage}); + exec_trigger(rTestApp.m_exec, {pipeline, stage}); } enqueue_dirty(rTestApp.m_tasks, *rTestApp.m_graph, rTestApp.m_exec); @@ -197,10 +197,10 @@ static ScenarioMap_t make_scenarios() auto const tgShSp = shapeSpawn.get_pipelines(); exec_resize(rTestApp.m_tasks, rTestApp.m_exec); - set_dirty(rTestApp.m_exec, tgCS.drawEnt(New)); - set_dirty(rTestApp.m_exec, tgCS.drawEnt(Use)); - set_dirty(rTestApp.m_exec, tgCS.drawEntResized(Working)); - set_dirty(rTestApp.m_exec, tgShSp.spawnRequest(Use_)); + exec_trigger(rTestApp.m_exec, tgCS.drawEnt(New)); + exec_trigger(rTestApp.m_exec, tgCS.drawEnt(Use)); + exec_trigger(rTestApp.m_exec, tgCS.drawEntResized(Working)); + exec_trigger(rTestApp.m_exec, tgShSp.spawnRequest(Use_)); return [] (TestApp& rTestApp) { diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 3ad6955e..82f2b11d 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -146,8 +146,9 @@ Session setup_scene_renderer( rBuilder.task() .name ("Resize Scene Render containers to fit drawable entities") - .run_on ({tgCS.drawEntResized(Working)}) - .sync_with ({tgSR.scnRender(New), tgSR.drawTransforms(Resize), tgSR.entMesh(New)}) + .run_on ({tgSR.drawTransforms(Resize)}) + .conditions ({tgCS.drawEntResized(Working)}) + .sync_with ({tgCS.drawEnt(New), tgSR.scnRender(New), tgSR.entMesh(New)}) .push_to (out.m_tasks) .args ({ idDrawing, idScnRender}) .func([] (ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index daefd4b3..71aa4e65 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -64,8 +64,9 @@ void randomized_singlethreaded_execute(Tasks const& tasks, TaskGraph const& grap if (runTasksLeft != 0) { TaskId const randomTask = rExec.tasksQueuedRun.at(rRand() % runTasksLeft); - TriggerOut_t const status = runTask(randomTask); - mark_completed_task(tasks, graph, rExec, randomTask, status); + bool const canRun = conditions_satisfied(tasks, graph, rExec, randomTask); + TriggerOut_t const status = canRun ? runTask(randomTask) : gc_triggerNone; + complete_task(tasks, graph, rExec, randomTask, status); } enqueue_dirty(tasks, graph, rExec); @@ -166,7 +167,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) { input = 1 + randGen() % 30; - set_dirty(exec, pl.vec(Fill)); + exec_trigger(exec, pl.vec(Fill)); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output, &checksRun] (TaskId const task) -> TriggerOut_t @@ -223,7 +224,7 @@ TEST(Tasks, BasicSingleThreadedTriggers) auto const pl = builder.create_pipelines(); - // These tasks run in a loop, triggering each other in a loop + // These tasks run in a loop, triggering each other to run continuously builder.task() .run_on ({pl.normal(Schedule)}) @@ -232,12 +233,12 @@ TEST(Tasks, BasicSingleThreadedTriggers) { if (rRand() % 2 == 0) { - return 0b01; // trigger {pl.normal, Write} + return 0b01; // trigger pl.normal(Write) only } else { rState.optionalFlagExpect = true; - return 0b11; // trigger {pl.normal, Write} and {pl.optional, Write} + return 0b11; // trigger pl.normal(Write) and pl.optional(Write) } }); @@ -291,7 +292,140 @@ TEST(Tasks, BasicSingleThreadedTriggers) TestState world; - set_dirty(exec, pl.normal(Schedule)); + exec_trigger(exec, pl.normal(Schedule)); + enqueue_dirty(tasks, graph, exec); + + randomized_singlethreaded_execute( + tasks, graph, exec, randGen, sc_taskRuns, + [&functions, &world, &randGen] (TaskId const task) -> TriggerOut_t + { + return functions[task](world, randGen); + }); + + // Assure that the tasks above actually ran, and didn't just skip everything + // Max of 5 tasks run each loop + ASSERT_GT(world.checks, sc_taskRuns / 5); + +} + +namespace test_c +{ + +struct TestState +{ + int checks { 0 }; + bool normalFlag { false }; + bool optionalFlagExpect { false }; + bool optionalFlag { false }; +}; + + +enum class StgRun { Wait, Run }; +enum class StgGuide { Clear, Schedule, Write, Read }; + +struct Pipelines +{ + osp::PipelineDef run; + osp::PipelineDef normal; + osp::PipelineDef optional; +}; + +} // namespace test_gameworld + +// Test that features a looping 'normal' pipeline and an 'optional' pipeline that has a 50% chance of running +TEST(Tasks, BasicSingleThreadedCondition) +{ + using namespace test_c; + using enum StgRun; + using enum StgGuide; + + using BasicTraits_t = BasicBuilderTraits; + using Builder_t = BasicTraits_t::Builder; + using TaskFuncVec_t = BasicTraits_t::FuncVec_t; + + constexpr int sc_taskRuns = 128; + std::mt19937 randGen(69); + + Tasks tasks; + TaskEdges edges; + TaskFuncVec_t functions; + Builder_t builder{tasks, edges, functions}; + + auto const pl = builder.create_pipelines(); + + // These tasks run in a loop, triggering each other to run continuously + + builder.task() + .run_on ({pl.run(Run)}) + .sync_with ({pl.optional(Schedule)}) + .triggers ({pl.optional(Write)}) + .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + { + if (rRand() % 2 == 0) + { + return gc_triggerNone; + } + else + { + rState.optionalFlagExpect = true; + return gc_triggerAll; + } + }); + + builder.task() + .run_on ({pl.run(Run)}) + .sync_with ({pl.normal(Write)}) + .triggers ({pl.normal(Read)}) + .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + { + rState.normalFlag = true; + return gc_triggerAll; + }); + + builder.task() + .run_on ({pl.run(Run)}) + .conditions ({pl.optional(Write)}) + .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + { + rState.optionalFlag = true; + return gc_triggerNone; + }); + + + builder.task() + .run_on ({pl.run(Run)}) + .sync_with ({pl.optional(Read), pl.normal(Read)}) + .triggers ({pl.run(Run)}) + .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + { + EXPECT_TRUE(rState.normalFlag); + EXPECT_EQ(rState.optionalFlagExpect, rState.optionalFlag); + return gc_triggerAll; + }); + + builder.task() + .run_on ({pl.run(Run)}) + .sync_with ({pl.optional(Clear), pl.normal(Clear)}) + .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + { + ++ rState.checks; + rState.normalFlag = false; + rState.optionalFlagExpect = false; + rState.optionalFlag = false; + return gc_triggerNone; + }); + + + TaskGraph const graph = make_exec_graph(tasks, {&edges}); + + // Execute + + ExecContext exec; + exec_resize(tasks, graph, exec); + + TestState world; + + exec_trigger(exec, pl.run(Run)); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute( @@ -427,8 +561,8 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Enqueue initial tasks // This roughly indicates "Time has changed" and "Render requested" - set_dirty(exec, pl.time(StgSimple::Use)); - set_dirty(exec, pl.render(StgRender::Render)); + exec_trigger(exec, pl.time(StgSimple::Use)); + exec_trigger(exec, pl.render(StgRender::Render)); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute( From 2bf3063f0ce84990a34357542cbd0c790a7a437b Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 17 Jul 2023 20:50:20 -0700 Subject: [PATCH 23/35] Rewrite pipelines again --- src/osp/tasks/builder.h | 29 +- src/osp/tasks/execute.cpp | 498 +++++++++++++----- src/osp/tasks/execute.h | 38 +- src/osp/tasks/tasks.cpp | 222 +++----- src/osp/tasks/tasks.h | 56 +- src/osp/tasks/top_execute.cpp | 12 +- .../activescenes/identifiers.h | 32 +- .../activescenes/scenarios.cpp | 31 +- src/test_application/activescenes/scenarios.h | 1 + .../activescenes/scene_common.cpp | 34 +- .../activescenes/scene_misc.cpp | 11 +- .../activescenes/scene_physics.cpp | 31 +- .../activescenes/scene_physics.h | 2 + .../activescenes/scene_renderer.cpp | 32 +- .../activescenes/scene_renderer.h | 1 + test/tasks/main.cpp | 157 +----- 16 files changed, 591 insertions(+), 596 deletions(-) diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 8115e6e5..0d5fed56 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -127,14 +127,12 @@ struct TaskRefBase return static_cast(*this); } - TASKREF_T& run_on(ArrayView const specs) noexcept + TASKREF_T& run_on(TplPipelineStage const tpl) noexcept { - return add_edges(m_rBuilder.m_rEdges.m_runOn, specs); - } + m_rBuilder.m_rTasks.m_taskRunOn.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); + m_rBuilder.m_rTasks.m_taskRunOn[m_taskId] = tpl; - TASKREF_T& run_on(std::initializer_list specs) noexcept - { - return add_edges(m_rBuilder.m_rEdges.m_runOn, specs); + return static_cast(*this); } TASKREF_T& sync_with(ArrayView const specs) noexcept @@ -147,25 +145,6 @@ struct TaskRefBase return add_edges(m_rBuilder.m_rEdges.m_syncWith, specs); } - TASKREF_T& triggers(ArrayView const specs) noexcept - { - return add_edges(m_rBuilder.m_rEdges.m_triggers, specs); - } - - TASKREF_T& triggers(std::initializer_list specs) noexcept - { - return add_edges(m_rBuilder.m_rEdges.m_triggers, specs); - } - - TASKREF_T& conditions(ArrayView const specs) noexcept - { - return add_edges(m_rBuilder.m_rEdges.m_conditions, specs); - } - - TASKREF_T& conditions(std::initializer_list specs) noexcept - { - return add_edges(m_rBuilder.m_rEdges.m_conditions, specs); - } TaskId m_taskId; TASKBUILDER_T & m_rBuilder; diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 6fb65c54..2c70a008 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -36,27 +36,23 @@ void exec_resize(Tasks const& tasks, ExecContext &rOut) rOut.tasksQueuedRun .reserve(maxTasks); rOut.tasksQueuedBlocked.reserve(maxTasks); rOut.plData.resize(maxPipeline); - bitvector_resize(rOut.plDirty, maxPipeline); - - bitvector_resize(rOut.plDirtyNext, maxPipeline); + bitvector_resize(rOut.plAdvance, maxPipeline); + bitvector_resize(rOut.plAdvanceNext, maxPipeline); + bitvector_resize(rOut.plRequestRun, maxPipeline); } void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) { exec_resize(tasks, rOut); +} - rOut.anystgReqByTaskCount.resize(graph.anystgToPipeline.size(), 0); - - for (std::size_t pipelineInt : tasks.m_pipelineIds.bitview().zeros()) - { - int const stageCount = fanout_size(graph.pipelineToFirstAnystg, PipelineId(pipelineInt)); - if (stageCount == 0) - { - rOut.plData[PipelineId(pipelineInt)].currentStage = lgrn::id_null(); - } - } +void exec_run(ExecContext &rExec, PipelineId const pipeline) +{ + rExec.plRequestRun.set(std::size_t(pipeline)); + rExec.hasRequestRun = true; } + static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) { if (rExec.doLogging) @@ -65,128 +61,355 @@ static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) } } -void exec_trigger(ExecContext &rExec, TplPipelineStage tpl) +static constexpr bool pipeline_can_advance(TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept { - StageBits_t &rTriggered = rExec.plData[tpl.pipeline].triggered; + int const tasksQueued = rExecPl.tasksQueuedBlocked + rExecPl.tasksQueuedRun; - if ( ! rTriggered.test(std::size_t(tpl.stage)) ) - { - rExec.plDirty.set(std::size_t(tpl.pipeline)); - rTriggered.set(std::size_t(tpl.stage)); + return tasksQueued == 0 // Tasks done + && rExecPl.reqTasksLeft == 0 // Tasks required by stage are done + && rExecPl.reqByTaskLeft == 0; // Not required by any tasks - exec_log(rExec, ExecContext::ExternalTrigger{tpl.pipeline, tpl.stage}); - } } -static void calculate_next_stage(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept +static void pipeline_advance_stage(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { - ExecPipeline &rExecPl = rExec.plData[pipeline]; + ExecPipeline &rExecPl = rExec.plData[pipeline]; + + LGRN_ASSERT(pipeline_can_advance(graph, rExec, rExecPl)); + + int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); + LGRN_ASSERTM(stageCount != 0, "Pipelines with 0 stages shouldn't be running"); - rExecPl.nextStage = rExecPl.currentStage; - rExecPl.stageChanged = false; + bool const justStarting = rExecPl.stage == lgrn::id_null(); - if ( rExecPl.tasksQueuedBlocked != 0 - || rExecPl.tasksQueuedRun != 0 - || rExecPl.stageReqTaskCount != 0 - || rExecPl.currentStage == lgrn::id_null()) + auto const nextStage = StageId( justStarting ? 0 : (int(rExecPl.stage)+1) ); + + if (nextStage == StageId(stageCount)) { - // Can't advance. This stage still has (or requires other) tasks to complete - // or pipeline has no stages. + // Next stage is 1 past the last stage. Finished running + rExecPl.stage = lgrn::id_null(); + rExecPl.running = false; return; } - int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); + rExecPl.stage = nextStage; + rExecPl.tasksQueued = false; + + // asserted by pipeline_can_advance: + // * rExecPl.reqTasksLeft == 0; + // * rExecPl.reqByTaskLeft == 0; +} - if (stageCount == 0) +static void pipeline_advance_reqs(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +{ + ExecPipeline &rExecPl = rExec.plData[pipeline]; + + if ( ! rExecPl.running ) { return; } - while(true) + AnyStageId const anystg = anystg_from(graph, pipeline, rExecPl.stage); + + // Evaluate Task-requires-Stages + // These are tasks from other pipelines that require nextStage + + auto const revTaskReqStageView = ArrayView(fanout_view(graph.anystgToFirstRevTaskreqstg, graph.revTaskreqstgToTask, anystg)); + + // Number of tasks that require nextStage. This is decremented only when tasks finish + rExecPl.reqByTaskLeft = revTaskReqStageView.size(); + + // Unblock tasks that are alredy queued + for (TaskId const task : revTaskReqStageView) + { + if (rExec.tasksQueuedBlocked.contains(task)) + { + BlockedTask &rBlocked = rExec.tasksQueuedBlocked.get(task); + -- rBlocked.reqStagesLeft; + if (rBlocked.reqStagesLeft == 0) + { + exec_log(rExec, ExecContext::UnblockTask{task}); + ExecPipeline &rTaskPlExec = rExec.plData[rBlocked.pipeline]; + -- rTaskPlExec.tasksQueuedBlocked; + ++ rTaskPlExec.tasksQueuedRun; + rExec.tasksQueuedRun.emplace(task); + rExec.tasksQueuedBlocked.erase(task); + } + } + } + + // Evaluate Stage-requires-Tasks + // To be allowed to advance to the next-stage, these tasks must be complete. + + auto const stgreqtaskView = ArrayView(fanout_view(graph.anystgToFirstStgreqtask, graph.stgreqtaskData, anystg)); + + rExecPl.reqTasksLeft = stgreqtaskView.size(); + + // Decrement reqTasksLeft, as some of these tasks might already be complete + for (StageRequiresTask const& stgreqtask : stgreqtaskView) { - if (rExec.anystgReqByTaskCount[anystg_from(graph, pipeline, rExecPl.nextStage)] != 0) + ExecPipeline &rReqTaskExecPl = rExec.plData[stgreqtask.reqPipeline]; + + bool const reqTaskDone = [&rReqTaskExecPl, &stgreqtask, &rExec] () noexcept -> bool { - // Stop here, there are queued tasks that require this stage - return; + if ( ! rReqTaskExecPl.running ) + { + return true; // Not running, which means the whole pipeline finished already + } + else if (int(rReqTaskExecPl.stage) < int(stgreqtask.reqStage)) + { + return false; // Not yet reached required stage. Required task didn't run yet + } + else if (int(rReqTaskExecPl.stage) > int(stgreqtask.reqStage)) + { + return true; // Passed required stage. Required task finished + } + else if ( ! rReqTaskExecPl.tasksQueued ) + { + return false; // Required tasks not queued yet + } + else if ( rExec.tasksQueuedBlocked.contains(stgreqtask.reqTask) + || rExec.tasksQueuedRun .contains(stgreqtask.reqTask)) + { + return false; // Required task is queued and not yet finished running + } + else + { + return true; // On the right stage and task not running. This means it's done + } + }(); + + if (reqTaskDone) + { + -- rExecPl.reqTasksLeft; } + } +} + +static void pipeline_advance_run(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +{ + ExecPipeline &rExecPl = rExec.plData[pipeline]; + + if ( ! rExecPl.running ) + { + return; + } + + AnyStageId const anystg = anystg_from(graph, pipeline, rExecPl.stage); + + auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; - rExecPl.nextStage = stage_next(rExecPl.nextStage, stageCount); - rExecPl.stageChanged = true; + for (TaskId task : runTasks) + { + LGRN_ASSERTM( ! rExec.tasksQueuedBlocked.contains(task), "Impossible to queue a task that's already queued"); + LGRN_ASSERTM( ! rExec.tasksQueuedRun .contains(task), "Impossible to queue a task that's already queued"); - if (rExecPl.triggered.test(std::size_t(rExecPl.nextStage))) + // Evaluate Task-requires-Stages + // Some requirements may already be satisfied + auto const taskreqstageView = ArrayView(fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)); + int reqStagesLeft = taskreqstageView.size(); + + for (TaskRequiresStage const& req : taskreqstageView) { - // Stop on this stage to run tasks - return; + ExecPipeline const &rReqPlData = rExec.plData[req.reqPipeline]; + + if (rReqPlData.stage == req.reqStage) + { + reqStagesLeft --; + } } - // No triggered and no waiting tasks means infinite loop. Stage should still move by 1 though. - if ( ( ! rExecPl.triggered.any() ) - && ( rExecPl.stageReqTaskCount == 0 ) - && ( rExecPl.taskReqStageCount == 0 ) ) + if (reqStagesLeft == 0) + { + // Task can run right away + rExec.tasksQueuedRun.emplace(task); + ++ rExecPl.tasksQueuedRun; + } + else { - return; + rExec.tasksQueuedBlocked.emplace(task, BlockedTask{reqStagesLeft, pipeline}); + ++ rExecPl.tasksQueuedBlocked; } } + + rExecPl.tasksQueued = true; + + if (runTasks.size() == 0 && pipeline_can_advance(graph, rExec, rExecPl)) + { + // No tasks to run. RunTasks are responsible for setting this pipeline dirty once they're + // all done. If there is none, then this pipeline may get stuck if nothing sets it dirty, + // so set dirty right away. + rExec.plAdvanceNext.set(std::size_t(pipeline)); + rExec.hasPlAdvance = true; + } } -static void apply_pipeline_stage(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) + +static void run_pipeline_recurse(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { - ExecPipeline &rExecPl = rExec.plData[pipeline]; + ExecPipeline &rExecPl = rExec.plData[pipeline]; - StageId const oldStage = rExecPl.currentStage; - StageId const newStage = rExecPl.nextStage; + if (fanout_size(graph.pipelineToFirstAnystg, pipeline) != 0) + { + rExecPl.running = true; - if ( ! rExecPl.stageChanged ) + if (rExecPl.reqTasksLeft == 0) + { + rExec.plAdvance.set(std::size_t(pipeline)); + rExec.hasPlAdvance = true; + } + } + + auto const subscribers = ArrayView{fanout_view(graph.pipelineToFirstSub, graph.subToPipeline, pipeline)}; + + for (PipelineId const plSub : subscribers) { - return; + run_pipeline_recurse(graph, rExec, PipelineId(plSub)); } +} + +void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept +{ - rExecPl.triggerUsed = rExecPl.triggered.test(std::size_t(newStage)); - rExecPl.triggered.reset(std::size_t(newStage)); + exec_log(rExec, ExecContext::EnqueueStart{}); - exec_log(rExec, ExecContext::StageChange{pipeline, oldStage, newStage}); + [[maybe_unused]] std::size_t const queuedTasks = rExec.tasksQueuedRun.size() + rExec.tasksQueuedBlocked.size(); - rExecPl.currentStage = newStage; + LGRN_ASSERTM( ! ( queuedTasks!=0 && rExec.plRequestRun.count()!=0 ), + "Running new pipelines while already running is not yet supported ROFL!"); - LGRN_ASSERTV(rExecPl.stageReqTaskCount == 0, rExecPl.stageReqTaskCount); + if (rExec.hasRequestRun) + { + for (PipelineInt const plInt : rExec.plRequestRun.ones()) + { + auto const pipeline = PipelineId(plInt); + run_pipeline_recurse(graph, rExec, pipeline); + } + rExec.plRequestRun.reset(); + rExec.hasRequestRun = false; + } - // Evaluate Stage-requires-Tasks - // * Calculate stageReqTaskCount as the number of required task that are currently queued - AnyStageId const anystg = anystg_from(graph, pipeline, newStage); - int stageReqTaskCount = 0; - for (StageRequiresTask const& stgreqtask : fanout_view(graph.anystgToFirstStgreqtask, graph.stgreqtaskData, anystg)) + while (rExec.hasPlAdvance) { - if (rExec.tasksQueuedBlocked.contains(stgreqtask.reqTask) - || rExec.tasksQueuedRun .contains(stgreqtask.reqTask)) + exec_log(rExec, ExecContext::EnqueueCycle{}); + + rExec.hasPlAdvance = false; + + for (PipelineInt const plInt : rExec.plAdvance.ones()) { - ++ stageReqTaskCount; + pipeline_advance_stage(graph, rExec, PipelineId(plInt)); } + + for (PipelineInt const plInt : rExec.plAdvance.ones()) + { + pipeline_advance_reqs(graph, rExec, PipelineId(plInt)); + } + + for (PipelineInt const plInt : rExec.plAdvance.ones()) + { + pipeline_advance_run(graph, rExec, PipelineId(plInt)); + } + + std::copy(rExec.plAdvanceNext.ints().begin(), + rExec.plAdvanceNext.ints().end(), + rExec.plAdvance .ints().begin()); + rExec.plAdvanceNext.reset(); } - rExecPl.stageReqTaskCount = stageReqTaskCount; + exec_log(rExec, ExecContext::EnqueueEnd{}); +} - // Evaluate Task-requires-Stages - // * Increment counts for queued tasks that depend on this stage. This unblocks tasks +void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TriggerOut_t dirty) noexcept +{ + auto const try_advance = [&graph, &rExec] (PipelineId const pipeline, ExecPipeline &rExecPl) + { + if (pipeline_can_advance(graph, rExec, rExecPl)) + { + rExec.plAdvance.set(std::size_t(pipeline)); + rExec.hasPlAdvance = true; + } + }; - for (TaskId const& task : fanout_view(graph.anystgToFirstRevTaskreqstg, graph.revTaskreqstgToTask, anystg)) + LGRN_ASSERT(rExec.tasksQueuedRun.contains(task)); + rExec.tasksQueuedRun.erase(task); + + exec_log(rExec, ExecContext::CompleteTask{task}); + + auto const [pipeline, stage] = tasks.m_taskRunOn[task]; + ExecPipeline &rExecPl = rExec.plData[pipeline]; + + -- rExecPl.tasksQueuedRun; + + try_advance(pipeline, rExecPl); + + // Handle stages requiring this task + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) { - if (rExec.tasksQueuedBlocked.contains(task)) + PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); + ExecPipeline &rReqExecPl = rExec.plData[reqPl]; + + if (rReqExecPl.stage == reqStg) { - BlockedTask &rBlocked = rExec.tasksQueuedBlocked.get(task); - -- rBlocked.remainingTaskReqStg; - if (rBlocked.remainingTaskReqStg == 0) + -- rReqExecPl.reqTasksLeft; + + try_advance(reqPl, rReqExecPl); + } + else + { + LGRN_ASSERTMV(int(rReqExecPl.stage) < int(reqStg) && rReqExecPl.stage != lgrn::id_null(), + "Stage-requires-Task means that rReqExecPl.stage cannot advance any further than reqStg until task completes.", + int(task), int(rReqExecPl.stage), int(reqStg)); + } + } + + // Handle this task requiring stages + for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) + { + ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; + + LGRN_ASSERTMV(rReqExecPl.stage == req.reqStage, + "Task-requires-Stage means this task should have not run unless the stage is selected", + int(task), int(rReqExecPl.stage), int(req.reqStage)); + + -- rReqExecPl.reqByTaskLeft; + + try_advance(req.reqPipeline, rReqExecPl); + } + +} + +} // namespace osp + + + +#if 0 + + + // Step 2. Run pipelines requested to run + for (PipelineInt const plInt : rExec.plRunning.ones()) + { + auto const pipeline = PipelineId(plInt); + + StageInt const stgCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); + + // Run all tasks? + for (StageInt stgInt = 0; stgInt < stgCount; ++stgInt) + { + auto const stg = StageId(stgInt); + auto const anystg = anystg_from(graph, pipeline, stg); + auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; + + for (TaskId const task : runTasks) { - exec_log(rExec, ExecContext::UnblockTask{task}); - ExecPipeline &rTaskPlExec = rExec.plData[rBlocked.pipeline]; - -- rTaskPlExec.tasksQueuedBlocked; - ++ rTaskPlExec.tasksQueuedRun; - rExec.tasksQueuedRun.emplace(task); - rExec.tasksQueuedBlocked.erase(task); + // there's nothing to do here? } } } -} + rExec.plRunning.reset(); + + static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { @@ -241,7 +464,55 @@ static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecC continue; } - // Evaluate Stage-requires-Tasks + + } +} + + + + + // Evaluate Stage-requires-Tasks + // * Calculate stageReqTaskCount as the number of required task that are currently queued + AnyStageId const anystg = anystg_from(graph, pipeline, newStage); + int stageReqTaskCount = 0; + + for (StageRequiresTask const& stgreqtask : fanout_view(graph.anystgToFirstStgreqtask, graph.stgreqtaskData, anystg)) + { + if (rExec.tasksQueuedBlocked.contains(stgreqtask.reqTask) + || rExec.tasksQueuedRun .contains(stgreqtask.reqTask)) + { + ++ stageReqTaskCount; + } + } + + rExecPl.stageReqTaskCount = stageReqTaskCount; + + // Evaluate Task-requires-Stages + // * Increment counts for queued tasks that depend on this stage. This unblocks tasks + + for (TaskId const& task : fanout_view(graph.anystgToFirstRevTaskreqstg, graph.revTaskreqstgToTask, anystg)) + { + if (rExec.tasksQueuedBlocked.contains(task)) + { + BlockedTask &rBlocked = rExec.tasksQueuedBlocked.get(task); + -- rBlocked.remainingTaskReqStg; + if (rBlocked.remainingTaskReqStg == 0) + { + exec_log(rExec, ExecContext::UnblockTask{task}); + ExecPipeline &rTaskPlExec = rExec.plData[rBlocked.pipeline]; + -- rTaskPlExec.tasksQueuedBlocked; + ++ rTaskPlExec.tasksQueuedRun; + rExec.tasksQueuedRun.emplace(task); + rExec.tasksQueuedBlocked.erase(task); + } + } + } + + + + + + // Evaluate Stage-requires-Tasks // * Increment counts for currently running stages that require this task for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) { @@ -306,69 +577,9 @@ static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecC } } } - } -} -void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept -{ - auto const plDirtyOnes = rExec.plDirty.ones(); - exec_log(rExec, ExecContext::EnqueueStart{}); - - while (plDirtyOnes.begin() != plDirtyOnes.end()) - { - exec_log(rExec, ExecContext::EnqueueCycle{}); - - // Calculate next stages - for (std::size_t const pipelineInt : plDirtyOnes) - { - calculate_next_stage(graph, rExec, PipelineId(pipelineInt)); - } - - // Apply next stages - for (std::size_t const pipelineInt : plDirtyOnes) - { - apply_pipeline_stage(graph, rExec, PipelineId(pipelineInt)); - } - - // Run tasks. writes to plDirtyNext - for (std::size_t const pipelineInt : plDirtyOnes) - { - run_pipeline_tasks(tasks, graph, rExec, PipelineId(pipelineInt)); - } - - // Select next dirty pipelines - std::copy(rExec.plDirtyNext.ints().begin(), - rExec.plDirtyNext.ints().end(), - rExec.plDirty .ints().begin()); - rExec.plDirtyNext.reset(); - } - - exec_log(rExec, ExecContext::EnqueueEnd{}); -} - -bool conditions_satisfied(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task) noexcept -{ - for (auto const [pipeline, stage] : fanout_view(graph.taskToFirstCondition, graph.conditionToPlStage, task)) - { - ExecPipeline &rExecPl = rExec.plData[pipeline]; - if ( rExecPl.currentStage != stage - || ! rExecPl.triggerUsed ) - { - return false; - } - } - - return true; -} - -void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TriggerOut_t dirty) noexcept -{ - LGRN_ASSERT(rExec.tasksQueuedRun.contains(task)); - rExec.tasksQueuedRun.erase(task); - - exec_log(rExec, ExecContext::CompleteTask{task}); auto const try_set_dirty = [&rExec] (ExecPipeline &plExec, PipelineId pipeline) { @@ -431,8 +642,7 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe try_set_dirty(plExec, pipeline); } } -} -} // namespace osp +#endif diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 55abcad2..d1a589ed 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -40,47 +40,41 @@ namespace osp struct ExecPipeline { - StageBits_t triggered {0}; - StageId currentStage {0}; - StageId nextStage {0}; - int tasksQueuedRun {0}; int tasksQueuedBlocked {0}; - int stageReqTaskCount {0}; - int taskReqStageCount {0}; + int reqByTaskLeft {0}; + int reqTasksLeft {0}; - bool triggerUsed {false}; - bool stageChanged {false}; + StageId stage { lgrn::id_null() }; + bool tasksQueued { false }; + bool running { false }; + bool mightLoop { false }; }; struct BlockedTask { - int remainingTaskReqStg; + int reqStagesLeft; PipelineId pipeline; }; /** - * @brief The ExecContext class - * - * Pipelines are marked dirty (m_plDirty.set(pipeline int)) if... - * * All of its running tasks just finished - * * Not running but required by another pipeline + * @brief */ struct ExecContext { KeyedVec plData; - BitVector_t plDirty; - - entt::basic_sparse_set tasksQueuedRun; - entt::basic_storage tasksQueuedBlocked; + entt::basic_sparse_set tasksQueuedRun; + entt::basic_storage tasksQueuedBlocked; - KeyedVec anystgReqByTaskCount; + BitVector_t plAdvance; + BitVector_t plAdvanceNext; + bool hasPlAdvance {false}; - // used for enqueue_dirty as temporary values + BitVector_t plRequestRun; + bool hasRequestRun {false}; - BitVector_t plDirtyNext; // 'logging' @@ -151,7 +145,7 @@ void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); void exec_resize(Tasks const& tasks, ExecContext &rOut); -void exec_trigger(ExecContext &rExec, TplPipelineStage tpl); +void exec_run(ExecContext &rExec, PipelineId pipeline); void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index 8df564a1..830dbc20 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -33,9 +33,6 @@ struct TaskCounts { uint16_t requiresStages {0}; uint16_t requiredByStages {0}; - uint8_t triggers {0}; - uint8_t conditions {0}; - uint8_t runOn {0}; }; struct StageCounts @@ -49,7 +46,8 @@ struct PipelineCounts { std::array stageCounts; - uint8_t stages {0}; + uint16_t publishTo {0}; + uint8_t stages {0}; }; @@ -60,21 +58,19 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView std::size_t const maxPipelines = tasks.m_pipelineIds.capacity(); std::size_t const maxTasks = tasks.m_taskIds.capacity(); - // counts - KeyedVec plCounts; KeyedVec taskCounts; out.pipelineToFirstAnystg .resize(maxPipelines); plCounts .resize(maxPipelines+1); taskCounts .resize(maxTasks+1); - std::size_t totalTasksReqStage = 0; - std::size_t totalStageReqTasks = 0; - std::size_t totalRunTasks = 0; - std::size_t totalTriggers = 0; - std::size_t totalStages = 0; + std::size_t totalTasksReqStage = 0; + std::size_t totalStageReqTasks = 0; + std::size_t totalRunTasks = 0; + std::size_t totalSubscriptions = 0; + std::size_t totalStages = 0; - // Count up which pipeline/stages each task runs on + // 1. Count total number of stages auto const count_stage = [&plCounts] (PipelineId const pipeline, StageId const stage) { @@ -82,105 +78,63 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView rStageCount = std::max(rStageCount, uint8_t(uint8_t(stage) + 1)); }; - for (TaskEdges const* pEdges : data) + // Count stages from task run-ons + for (TaskInt const taskInt : tasks.m_taskIds.bitview().zeros()) { - totalRunTasks += pEdges->m_runOn.size(); - for (auto const [task, pipeline, stage] : pEdges->m_runOn) - { - plCounts[pipeline].stageCounts[std::size_t(stage)].runTasks ++; - taskCounts[task].runOn ++; - count_stage(pipeline, stage); - } + auto const task = TaskId(taskInt); + auto const [runPipeline, runStage] = tasks.m_taskRunOn[task]; - for (auto const [task, pipeline, stage] : pEdges->m_conditions) - { - taskCounts[task].conditions ++; - count_stage(pipeline, stage); - } + count_stage(runPipeline, runStage); - for (auto const [task, pipeline, stage] : pEdges->m_syncWith) - { - count_stage(pipeline, stage); - } + ++ plCounts[runPipeline].stageCounts[std::size_t(runStage)].runTasks; + ++ totalRunTasks; + } - totalTriggers += pEdges->m_triggers.size(); - for (auto const [task, pipeline, stage] : pEdges->m_triggers) + // Count stages from syncs + for (TaskEdges const* pEdges : data) + { + for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { - taskCounts[task].triggers ++; count_stage(pipeline, stage); } } - // Count total stages - for (PipelineCounts const& plCount : plCounts) { totalStages += plCount.stages; } - // Count TaskRequiresStages and StageRequiresTasks - - auto const count_stagereqtask = [&plCounts, &taskCounts, &totalStageReqTasks] - (PipelineId const pl, StageId const stg, TaskId const task) - { - StageCounts &rStageCounts = plCounts[pl].stageCounts[std::size_t(stg)]; - TaskCounts &rTaskCounts = taskCounts[task]; - - rStageCounts.requiresTasks ++; - rTaskCounts .requiredByStages ++; - totalStageReqTasks ++; - }; - - - auto const count_taskreqstage = [&plCounts, &taskCounts, &totalTasksReqStage] - (TaskId const task, PipelineId const pl, StageId const stg) - { - StageCounts &rStageCounts = plCounts[pl].stageCounts[std::size_t(stg)]; - TaskCounts &rTaskCounts = taskCounts[task]; - - rTaskCounts .requiresStages ++; - rStageCounts.requiredByTasks ++; - totalTasksReqStage ++; - }; + // 2. Count TaskRequiresStages and StageRequiresTasks and Subscriptions for (TaskEdges const* pEdges : data) { - // Each run-on adds... - // * TaskRequiresStage makes task require pipeline to be on stage to be allowed to run - // only if the task runs on multiple pipelines. This means all pipelines need to be - for (auto const [task, pipeline, stage] : pEdges->m_runOn) - { - if (taskCounts[task].runOn > 1) - { - count_taskreqstage(task, pipeline, stage); - } - } - - // Each sync-with adds... - // * TaskRequiresStage makes task require pipeline to be on stage to be allowed to run - // * StageRequiresTask for stage to wait for task to complete for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { - count_stagereqtask(pipeline, stage, task); - count_taskreqstage(task, pipeline, stage); - } + StageCounts &rStageCounts = plCounts[pipeline].stageCounts[std::size_t(stage)]; + TaskCounts &rTaskCounts = taskCounts[task]; + + // TaskRequiresStage makes task require pipeline to be on stage to be allowed to run + ++ rTaskCounts .requiresStages; + ++ rStageCounts.requiredByTasks; + + + // StageRequiresTask for stage to wait for task to complete + ++ rStageCounts.requiresTasks; + ++ rTaskCounts .requiredByStages; - // Condition implies sync-with - for (auto const [task, pipeline, stage] : pEdges->m_conditions) - { - count_stagereqtask(pipeline, stage, task); - count_taskreqstage(task, pipeline, stage); } + totalTasksReqStage += pEdges->m_syncWith.size(); + totalStageReqTasks += pEdges->m_syncWith.size(); - // Each triggers adds... - // * StageRequiresTask on previous stage to wait for task to complete - for (auto const [task, pipeline, stage] : pEdges->m_triggers) + for (auto const [publisher, subscriber] : pEdges->m_subscriptions) { - count_stagereqtask(pipeline, stage_prev(stage, plCounts[pipeline].stages), task); + ++ plCounts[publisher].publishTo; } + totalSubscriptions += pEdges->m_subscriptions.size(); } - // Allocate + + // 3. Allocate // The +1 is needed for 1-to-many connections to store the total number of other elements they // index. This also simplifies logic in fanout_view(...) @@ -189,12 +143,8 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView out.anystgToPipeline .resize(totalStages+1, lgrn::id_null()); out.anystgToFirstRuntask .resize(totalStages+1, lgrn::id_null()); out.runtaskToTask .resize(totalRunTasks, lgrn::id_null()); - out.taskToFirstRunstage .resize(maxTasks+1, lgrn::id_null()); - out.runstageToAnystg .resize(totalRunTasks, lgrn::id_null()); - out.taskToFirstTrigger .resize(maxTasks+1, lgrn::id_null()); - out.triggerToPlStage .resize(totalTriggers, {lgrn::id_null(), lgrn::id_null()}); - out.taskToFirstCondition .resize(maxTasks+1, lgrn::id_null()); - out.conditionToPlStage .resize(totalTriggers, {lgrn::id_null(), lgrn::id_null()}); + out.pipelineToFirstSub .resize(maxPipelines+1, lgrn::id_null()); + out.subToPipeline .resize(totalSubscriptions, lgrn::id_null()); out.anystgToFirstStgreqtask .resize(totalStages+1, lgrn::id_null()); out.stgreqtaskData .resize(totalStageReqTasks, {}); out.taskToFirstRevStgreqtask .resize(maxTasks+1, lgrn::id_null()); @@ -204,7 +154,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView out.anystgToFirstRevTaskreqstg .resize(totalStages+1, lgrn::id_null()); out.revTaskreqstgToTask .resize(totalTasksReqStage, lgrn::id_null()); - // Calcualte one-to-many partitions + // 4. Calculate one-to-many partitions fanout_partition( out.pipelineToFirstAnystg, @@ -215,30 +165,20 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView out.anystgToFirstRuntask, [&plCounts, &out] (AnyStageId stg) { - PipelineId const pl = out.anystgToPipeline[stg]; + PipelineId const pl = out.anystgToPipeline[stg]; if (pl == lgrn::id_null()) { return uint16_t(0); } - StageId const stgLocal = stage_from(out, pl, stg); + StageId const stgLocal = stage_from(out, pl, stg); return plCounts[pl].stageCounts[std::size_t(stgLocal)].runTasks; }, [] (AnyStageId, RunTaskId) { }); fanout_partition( - out.taskToFirstRunstage, - [&taskCounts, &out] (TaskId task) { return taskCounts[task].runOn; }, - [&out] (TaskId, RunStageId) { }); - - fanout_partition( - out.taskToFirstTrigger, - [&taskCounts, &out] (TaskId task) { return taskCounts[task].triggers; }, - [&out] (TaskId, TriggerId) { }); - - fanout_partition( - out.taskToFirstCondition, - [&taskCounts, &out] (TaskId task) { return taskCounts[task].conditions; }, - [&out] (TaskId, ConditionId) { }); + out.pipelineToFirstSub, + [&plCounts] (PipelineId pl) { return plCounts[pl].publishTo; }, + [&out] (PipelineId, PipelineSubId) { }); fanout_partition( out.anystgToFirstStgreqtask, @@ -276,7 +216,20 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView }, [&out] (AnyStageId, ReverseTaskReqStageId) { }); - // Push + // 5. Push + + for (TaskInt const taskInt : tasks.m_taskIds.bitview().zeros()) + { + auto const task = TaskId(taskInt); + auto const run = tasks.m_taskRunOn[task]; + auto const anystg = anystg_from(out, run.pipeline, run.stage); + StageCounts &rStageCounts = plCounts[run.pipeline].stageCounts[std::size_t(run.stage)]; + RunTaskId const runtask = id_from_count(out.anystgToFirstRuntask, anystg, rStageCounts.runTasks); + + out.runtaskToTask[runtask] = task; + + -- rStageCounts.runTasks; + } auto add_stagereqtask = [&plCounts, &taskCounts, &totalStageReqTasks, &out] (PipelineId const pl, StageId const stg, TaskId const task) @@ -320,27 +273,6 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView for (TaskEdges const* pEdges : data) { - for (auto const [task, pipeline, stage] : pEdges->m_runOn) - { - AnyStageId const anystg = anystg_from(out, pipeline, stage); - StageCounts &rStageCounts = plCounts[pipeline].stageCounts[std::size_t(stage)]; - TaskCounts &rTaskCounts = taskCounts[task]; - - RunTaskId const runTask = id_from_count(out.anystgToFirstRuntask, anystg, rStageCounts.runTasks); - RunStageId const runStage = id_from_count(out.taskToFirstRunstage, task, rTaskCounts.runOn); - - out.runtaskToTask[runTask] = task; - out.runstageToAnystg[runStage] = anystg; - - -- rStageCounts.runTasks; - -- rTaskCounts.runOn; - -- totalRunTasks; - - if (rTaskCounts.runOn > 1) - { - add_taskreqstage(task, pipeline, stage); - } - } for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { @@ -348,33 +280,12 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView add_taskreqstage(task, pipeline, stage); } - for (auto const [task, pipeline, stage] : pEdges->m_conditions) - { - add_stagereqtask(pipeline, stage, task); - add_taskreqstage(task, pipeline, stage); - - TaskCounts &rTaskCounts = taskCounts[task]; - ConditionId const condition = id_from_count(out.taskToFirstCondition, task, rTaskCounts.conditions); - out.conditionToPlStage[condition] = { pipeline, stage }; - -- rTaskCounts.conditions; - } - - for (auto const [task, pipeline, stage] : pEdges->m_triggers) - { - add_stagereqtask(pipeline, stage_prev(stage, plCounts[pipeline].stages), task); - - TaskCounts &rTaskCounts = taskCounts[task]; - TriggerId const trigger = id_from_count(out.taskToFirstTrigger, task, rTaskCounts.triggers); - out.triggerToPlStage[trigger] = { pipeline, stage }; - -- rTaskCounts.triggers; - } } [[maybe_unused]] auto const all_counts_zero = [&] () { - if ( totalRunTasks != 0 - || totalStageReqTasks != 0 - || totalTasksReqStage != 0) + if ( totalStageReqTasks != 0 + || totalTasksReqStage != 0 ) { return false; } @@ -383,8 +294,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView for (StageCounts const& stgCount : plCount.stageCounts) { if ( stgCount.requiredByTasks != 0 - || stgCount.requiresTasks != 0 - || stgCount.runTasks != 0) + || stgCount.requiresTasks != 0 ) { return false; } @@ -393,9 +303,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView for (TaskCounts const& taskCount : taskCounts) { if ( taskCount.requiredByStages != 0 - || taskCount.requiredByStages != 0 - || taskCount.runOn != 0 - || taskCount.conditions != 0) + || taskCount.requiredByStages != 0 ) { return false; } diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 4c038496..4b11c6e3 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -73,17 +73,6 @@ struct PipelineInfo stage_type_t stageType; }; -struct Tasks -{ - lgrn::IdRegistryStl m_taskIds; - lgrn::IdRegistryStl m_pipelineIds; - lgrn::IdRegistryStl m_semaIds; - - KeyedVec m_semaLimits; - - KeyedVec m_pipelineInfo; -}; - struct TplTaskPipelineStage { TaskId task; @@ -97,6 +86,11 @@ struct TplPipelineStage StageId stage; }; +struct PipelineSubscribe +{ + PipelineId publisher; + PipelineId subscriber; +}; struct TplTaskSemaphore { @@ -104,12 +98,24 @@ struct TplTaskSemaphore SemaphoreId semaphore; }; +struct Tasks +{ + lgrn::IdRegistryStl m_taskIds; + lgrn::IdRegistryStl m_pipelineIds; + lgrn::IdRegistryStl m_semaIds; + + KeyedVec m_semaLimits; + + KeyedVec m_pipelineInfo; + + KeyedVec m_taskRunOn; +}; + struct TaskEdges { - std::vector m_runOn; std::vector m_syncWith; - std::vector m_triggers; - std::vector m_conditions; + std::vector m_subscriptions; + std::vector m_semaphoreEdges; }; @@ -117,8 +123,8 @@ enum class AnyStageId : uint32_t { }; enum class RunTaskId : uint32_t { }; enum class RunStageId : uint32_t { }; -enum class TriggerId : uint32_t { }; -enum class ConditionId : uint32_t { }; + +enum class PipelineSubId : uint32_t { }; enum class StageReqTaskId : uint32_t { }; enum class ReverseStageReqTaskId : uint32_t { }; @@ -159,20 +165,10 @@ struct TaskGraph KeyedVec anystgToFirstRuntask; KeyedVec runtaskToTask; - // Each task runs on many stages - // TaskId --> TaskRunStageId --> many AnyStageId - KeyedVec taskToFirstRunstage; - KeyedVec runstageToAnystg; - - // Tasks trigger many stages of pipelines - // TaskId --> TriggerId --> many TplPipelineStage - KeyedVec taskToFirstTrigger; - KeyedVec triggerToPlStage; - - // Tasks can have many conditions - // TaskId --> ConditionId --> many TplPipelineStage - KeyedVec taskToFirstCondition; - KeyedVec conditionToPlStage; + // Each pipeline has multiple subscriber pipelines + // PipelineId --> PipelineSubId --> many PipelineId + KeyedVec pipelineToFirstSub; + KeyedVec subToPipeline; // Each stage has multiple entrance requirements. // AnyStageId <--> many StageEnterReqId diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index 6b517aa5..5b28a884 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -74,13 +74,21 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec : entt::any{}); } - // Task actually runs here. Results are not yet used for anything. - TriggerOut_t const status = (rTopTask.m_func != nullptr) ? rTopTask.m_func(worker, topDataRefs) : 0; + bool const shouldRun = (rTopTask.m_func != nullptr) && conditions_satisfied(tasks, graph, rExec, task); + + // Task actually runs here + TriggerOut_t const status = shouldRun ? rTopTask.m_func(worker, topDataRefs) : 0; complete_task(tasks, graph, rExec, task, status); top_write_log(std::cout, tasks, rTaskData, graph, rExec); rExec.logMsg.clear(); } + else + { + std::cout << "RIP pipelines. something deadlocked UwU\n"; + top_write_pipeline_states(std::cout, tasks, rTaskData, graph, rExec); + std::abort(); + } enqueue_dirty(tasks, graph, rExec); top_write_log(std::cout, tasks, rTaskData, graph, rExec); diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 97472ede..d99dad62 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -31,20 +31,21 @@ namespace testapp { + enum class EStgFlag : uint8_t { - Done, - Working + Wait_, + Write }; -OSP_DECLARE_STAGE_NAMES(EStgFlag, "Done", "Working"); +OSP_DECLARE_STAGE_NAMES(EStgFlag, "Wait", "Write"); enum class EStgEvnt : uint8_t { - Waiting, - Fired + Wait, + Run }; -OSP_DECLARE_STAGE_NAMES(EStgEvnt, "Waiting", "Fired"); +OSP_DECLARE_STAGE_NAMES(EStgEvnt, "Wait", "Run"); /** @@ -100,11 +101,12 @@ using osp::PipelineDef; idDeltaTimeIn struct PlScene { - PipelineDef cleanup {"cleanup - Scene cleanup before destruction"}; - PipelineDef time {"time - External Delta Time In"}; - PipelineDef resyncAll {"resyncAll - Resynchronize with "}; + PipelineDef cleanup {"cleanup - Scene cleanup before destruction"}; + PipelineDef resyncAll {"resyncAll - Resynchronize with renderer"}; - //PipelineDef sync; + PipelineDef updTime {"time - External Delta Time In"}; + PipelineDef updActive {"updActive - Updates on ActiveEnt and components"}; + PipelineDef updDraw {"updDraw - Updates on DrawEnt and components"}; }; #define TESTAPP_DATA_COMMON_SCENE 6, \ @@ -128,8 +130,8 @@ struct PlCommonScene PipelineDef entTextureDirty {"entTextureDirty"}; PipelineDef entMeshDirty {"entMeshDirty"}; - PipelineDef meshResDirty {"meshResDirty"}; - PipelineDef textureResDirty {"textureResDirty"}; + PipelineDef meshResDirty {"meshResDirty"}; + PipelineDef textureResDirty {"textureResDirty"}; PipelineDef material {"material"}; PipelineDef materialDirty {"materialDirty"}; @@ -280,8 +282,8 @@ struct PlNewton idUserInput struct PlWindowApp { - PipelineDef inputs {"inputs - User inputs in"}; - PipelineDef display {"display - Display new frame"}; + PipelineDef inputs {"inputs - User inputs in"}; + PipelineDef display {"display - Display new frame"}; }; @@ -290,7 +292,7 @@ struct PlWindowApp idActiveApp, idRenderGl struct PlMagnum { - PipelineDef cleanup {"cleanup Cleanup Magnum"}; + PipelineDef cleanup {"cleanup Cleanup Magnum"}; PipelineDef meshGL {"meshGL"}; PipelineDef textureGL {"textureGL"}; diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 45d53b4e..1efe5de5 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -77,12 +77,16 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c // Resynchronize scene with new renderer exec_resize(rTestApp.m_tasks, rTestApp.m_exec); - exec_trigger(rTestApp.m_exec, tgScn.resyncAll(EStgFlag::Working)); + exec_trigger(rTestApp.m_exec, tgScn.resyncAll(EStgEvnt::Run)); + exec_trigger(rTestApp.m_exec, tgScn.updDraw(EStgEvnt::Run)); + exec_trigger(rTestApp.m_exec, tgScn.updActive(EStgEvnt::Run)); run.insert(run.end(), { - tgScn.time.tpl(EStgFlag::Working), - tgWin.inputs.tpl(EStgFlag::Working), - tgWin.display.tpl(EStgFlag::Working)}); + tgScn.updTime (EStgEvnt::Run), + tgScn.updActive (EStgEvnt::Run), + tgScn.updDraw (EStgEvnt::Run), + tgWin.inputs (EStgEvnt::Run), + tgWin.display (EStgEvnt::Run)}); // run gets copied but who cares lol rActiveApp.set_on_draw( [&rTestApp, run = std::move(run), resync = tgScn.resyncAll] @@ -125,6 +129,7 @@ static ScenarioMap_t make_scenarios() PipelineInfo::sm_stageNames.resize(32); register_stage_enum(); + register_stage_enum(); register_stage_enum(); register_stage_enum(); register_stage_enum(); @@ -180,8 +185,8 @@ static ScenarioMap_t make_scenarios() // Compose together lots of Sessions scene = setup_scene (builder, rTopData); commonScene = setup_common_scene (builder, rTopData, scene, idResources, defaultPkg); - physics = setup_physics (builder, rTopData, commonScene); - shapeSpawn = setup_shape_spawn (builder, rTopData, commonScene, physics, sc_matVisualizer); + physics = setup_physics (builder, rTopData, scene, commonScene); + shapeSpawn = setup_shape_spawn (builder, rTopData, scene, commonScene, physics, sc_matVisualizer); //droppers = setup_droppers (builder, rTopData, commonScene, shapeSpawn); //bounds = setup_bounds (builder, rTopData, commonScene, physics, shapeSpawn); @@ -193,14 +198,14 @@ static ScenarioMap_t make_scenarios() create_materials(rTopData, commonScene, sc_materialCount); add_floor(rTopData, commonScene, shapeSpawn, sc_matVisualizer, idResources, defaultPkg); - auto const tgCS = commonScene.get_pipelines(); - auto const tgShSp = shapeSpawn.get_pipelines(); + auto const tgScn = scene .get_pipelines(); + auto const tgCS = commonScene .get_pipelines(); + auto const tgShSp = shapeSpawn .get_pipelines(); exec_resize(rTestApp.m_tasks, rTestApp.m_exec); - exec_trigger(rTestApp.m_exec, tgCS.drawEnt(New)); - exec_trigger(rTestApp.m_exec, tgCS.drawEnt(Use)); - exec_trigger(rTestApp.m_exec, tgCS.drawEntResized(Working)); exec_trigger(rTestApp.m_exec, tgShSp.spawnRequest(Use_)); + exec_trigger(rTestApp.m_exec, tgScn.updDraw(EStgEvnt::Run)); + exec_trigger(rTestApp.m_exec, tgScn.updActive(EStgEvnt::Run)); return [] (TestApp& rTestApp) { @@ -223,8 +228,8 @@ static ScenarioMap_t make_scenarios() scnRender = setup_scene_renderer (builder, rTopData, windowApp, magnum, scene, commonScene, idResources); cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, scnRender); cameraFree = setup_camera_free (builder, rTopData, windowApp, scene, cameraCtrl); - shVisual = setup_shader_visualizer (builder, rTopData, magnum, commonScene, scnRender, sc_matVisualizer); - camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); + shVisual = setup_shader_visualizer (builder, rTopData, magnum, scene, commonScene, scnRender, sc_matVisualizer); + //camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); setup_magnum_draw(rTestApp, scene, scnRender); }; diff --git a/src/test_application/activescenes/scenarios.h b/src/test_application/activescenes/scenarios.h index 1f3bb884..9b7c3db8 100644 --- a/src/test_application/activescenes/scenarios.h +++ b/src/test_application/activescenes/scenarios.h @@ -41,6 +41,7 @@ namespace scenes { using enum EStgCont; using enum EStgIntr; + using enum EStgEvnt; using enum EStgFlag; using enum EStgRender; } diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index 2eaa930d..28bd474c 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -77,9 +77,9 @@ Session setup_common_scene( rBuilder.task() .name ("Set materials, meshes, and textures dirty") - .run_on ({tgScn.resyncAll(Working)}) - .sync_with ({tgCS.texture(Modify), tgCS.mesh(Modify)}) - .triggers ({tgCS.entTextureDirty(Use_), tgCS.entMeshDirty(Use_), tgCS.materialDirty(Use_), tgCS.textureResDirty(Working), tgCS.meshResDirty(Working)}) + .run_on ({tgScn.resyncAll(Run)}) + //.sync_with ({tgCS.texture(Modify), tgCS.mesh(Modify)}) + .triggers ({tgCS.entTextureDirty(Use_), tgCS.entMeshDirty(Use_), tgCS.materialDirty(Use_), tgCS.meshResDirty(Run), tgCS.textureResDirty(Run), tgCS.transform(Use), tgCS.drawEnt(New), tgCS.drawEnt(Use), tgCS.drawEntResized(Write), }) .push_to (out.m_tasks) .args ({ idDrawing }) .func([] (ACtxDrawing& rDrawing) noexcept @@ -90,8 +90,9 @@ Session setup_common_scene( rBuilder.task() .name ("Delete ActiveEnt IDs") - .run_on ({tgCS.activeEntDelete(Use_)}) - .sync_with ({tgCS.activeEnt (Delete)}) + .run_on ({tgScn.updActive(Run)}) + .conditions ({tgCS.activeEntDelete(Use_)}) + .sync_with ({tgCS.activeEnt(Delete)}) .push_to (out.m_tasks) .args ({ idBasic, idActiveEntDel }) .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -107,8 +108,9 @@ Session setup_common_scene( rBuilder.task() .name ("Delete basic components") - .run_on ({tgCS.activeEntDelete(Use_)}) - .sync_with ({tgCS.transform (Delete)}) + .run_on ({tgScn.updActive(Run)}) + .conditions ({tgCS.activeEntDelete(Use_)}) + .sync_with ({tgCS.transform(Delete)}) .push_to (out.m_tasks) .args ({ idBasic, idActiveEntDel }) .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -118,8 +120,9 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEntity of deleted ActiveEnts") - .run_on ({tgCS.activeEntDelete(Use_)}) - .sync_with ({tgCS.drawEntDelete (Modify_)}) + .run_on ({tgScn.updActive(Run)}) + .conditions ({tgCS.activeEntDelete(Use_) }) + .sync_with ({tgCS.drawEntDelete(Modify_)}) .triggers ({tgCS.drawEntDelete (Use_), tgCS.drawEntDelete(Clear)}) .push_to (out.m_tasks) .args ({ idDrawing, idActiveEntDel, idDrawEntDel }) @@ -139,7 +142,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete drawing components") - .run_on ({tgCS.drawEntDelete(Use_)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.drawEntDelete(Use_)}) .sync_with ({tgCS.mesh(Delete), tgCS.texture(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) @@ -150,7 +154,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEntity IDs") - .run_on ({tgCS.drawEntDelete(Use_)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.drawEntDelete(Use_)}) .sync_with ({tgCS.drawEnt (Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) @@ -167,7 +172,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEnt from materials") - .run_on ({tgCS.drawEntDelete(Use_)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.drawEntDelete(Use_)}) .sync_with ({tgCS.material (Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) @@ -217,7 +223,7 @@ Session setup_common_scene( rBuilder.task() .name ("Clean up scene and resource owners") - .run_on ({tgScn.cleanup(Working)}) + .run_on ({tgScn.cleanup(Run)}) .sync_with ({tgCS.mesh(Delete), tgCS.texture(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idResources}) @@ -229,7 +235,7 @@ Session setup_common_scene( rBuilder.task() .name ("Clean up NamedMeshes mesh and texture owners") - .run_on ({tgScn.cleanup(Working)}) + .run_on ({tgScn.cleanup(Run)}) .push_to (out.m_tasks) .args ({ idDrawing, idNMesh }) .func([] (ACtxDrawing& rDrawing, NamedMeshes& rNMesh) noexcept diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index 0424aca8..89946606 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -151,7 +151,8 @@ Session setup_camera_ctrl( OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); OSP_DECLARE_GET_DATA_IDS(scnRender, TESTAPP_DATA_COMMON_RENDERER); - auto const tgSR = scnRender.get_pipelines(); + auto const tgSR = scnRender.get_pipelines(); + auto const tgWin = windowApp.get_pipelines(); auto &rUserInput = top_get< osp::input::UserInputHandler >(topData, idUserInput); @@ -163,8 +164,8 @@ Session setup_camera_ctrl( rBuilder.task() .name ("Position Rendering Camera according to Camera Controller") - .run_on ({tgCmCt.camCtrl(Use)}) - .sync_with ({tgSR.camera(Modify)}) + .run_on ({tgWin.inputs(Run)}) + .sync_with ({tgCmCt.camCtrl(Use), tgSR.camera(Modify)}) .push_to (out.m_tasks) .args ({ idCamCtrl, idCamera }) .func([] (ACtxCameraController const& rCamCtrl, Camera &rCamera) noexcept @@ -192,7 +193,7 @@ Session setup_camera_free( rBuilder.task() .name ("Move Camera controller") - .run_on ({tgWin.inputs(Working)}) + .run_on ({tgWin.inputs(Run)}) .sync_with ({tgCmCt.camCtrl(Modify)}) .triggers ({tgCmCt.camCtrl(Use)}) .push_to (out.m_tasks) @@ -231,7 +232,7 @@ Session setup_thrower( rBuilder.task() .name ("Throw spheres when pressing space") - .run_on ({tgWin.inputs(Working)}) + .run_on ({tgWin.inputs(Run)}) .sync_with ({tgCmCt.camCtrl(Use), tgShSp.spawnRequest(Modify_)}) .triggers ({tgShSp.spawnRequest(Use_)}) .push_to (out.m_tasks) diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index 227bbb73..c2b47e1e 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -53,10 +53,12 @@ namespace testapp::scenes Session setup_physics( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& scene, Session const& commonScene) { OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); - auto const tgCS = commonScene.get_pipelines(); + auto const tgScn = scene .get_pipelines(); + auto const tgCS = commonScene.get_pipelines(); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_PHYSICS); @@ -66,7 +68,8 @@ Session setup_physics( rBuilder.task() .name ("Delete Physics components") - .run_on ({tgCS.activeEntDelete(Use_)}) + .run_on ({tgScn.updActive(Run)}) + .conditions ({tgCS.activeEntDelete(Use_)}) .sync_with ({tgPhy.physics(Delete)}) .push_to (out.m_tasks) .args ({ idPhys, idActiveEntDel }) @@ -81,12 +84,14 @@ Session setup_physics( Session setup_shape_spawn( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& scene, Session const& commonScene, Session const& physics, MaterialId const materialId) { OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); + auto const tgScn = scene .get_pipelines(); auto const tgCS = commonScene .get_pipelines(); auto const tgPhy = physics .get_pipelines(); @@ -95,10 +100,10 @@ Session setup_shape_spawn( auto const tgShSp = out.create_pipelines(rBuilder); top_emplace< ACtxShapeSpawner > (topData, idSpawner, ACtxShapeSpawner{ .m_materialId = materialId }); - rBuilder.task() .name ("Create entities for requested shapes to spawn") - .run_on ({tgShSp.spawnRequest(Use_)}) + .run_on ({tgScn.updActive(Run)}) + .conditions ({tgShSp.spawnRequest(Use_)}) .sync_with ({tgCS.activeEnt(New), tgShSp.spawnedEnts(Resize)}) .triggers ({tgCS.activeEnt(Use), tgShSp.spawnedEnts(Use_)}) .push_to (out.m_tasks) @@ -115,7 +120,8 @@ Session setup_shape_spawn( rBuilder.task() .name ("Add hierarchy and transform to spawned shapes") - .run_on ({tgShSp.spawnRequest(Use_)}) + .run_on ({tgScn.updActive(Run)}) + .conditions ({tgShSp.spawnRequest(Use_)}) .sync_with ({tgShSp.spawnedEnts(Use_), tgCS.hierarchy(New), tgCS.transform(New)}) .triggers ({tgCS.transform(Use)}) .push_to (out.m_tasks) @@ -142,9 +148,10 @@ Session setup_shape_spawn( rBuilder.task() .name ("Add mesh and material to spawned shapes") - .run_on ({tgShSp.spawnRequest(Use_)}) + .run_on ({tgScn.updActive(Run)}) + .conditions ({tgShSp.spawnRequest(Use_)}) .sync_with ({tgShSp.spawnedEnts(Use_), tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New)}) - .triggers ({tgCS.drawEntResized(Working)}) + .triggers ({tgCS.drawEntResized(Write)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idSpawner, idNMesh }) .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept @@ -181,11 +188,14 @@ Session setup_shape_spawn( rDrawing.m_visible.set(std::size_t(drawEnt)); rDrawing.m_drawBasic[drawEnt].m_opaque = true; } + + return gc_triggerAll; }); rBuilder.task() .name ("Add physics to spawned shapes") - .run_on ({tgShSp.spawnRequest(Use_)}) + .run_on ({tgScn.updActive(Run)}) + .conditions ({tgShSp.spawnRequest(Use_)}) .sync_with ({tgShSp.spawnedEnts(Use_), tgPhy.physics(New)}) .push_to (out.m_tasks) .args ({ idBasic, idSpawner, idPhys }) @@ -215,9 +225,11 @@ Session setup_shape_spawn( } }); + rBuilder.task() .name ("Clear Shape Spawning vector after use") - .run_on ({tgShSp.spawnRequest(Clear)}) + .run_on ({tgScn.updActive(Run)}) + .conditions ({tgShSp.spawnRequest(Clear)}) .push_to (out.m_tasks) .args ({ idSpawner }) .func([] (ACtxShapeSpawner& rSpawner) noexcept @@ -225,6 +237,7 @@ Session setup_shape_spawn( rSpawner.m_spawnRequest.clear(); }); + return out; } diff --git a/src/test_application/activescenes/scene_physics.h b/src/test_application/activescenes/scene_physics.h index 073d5310..aae237ff 100644 --- a/src/test_application/activescenes/scene_physics.h +++ b/src/test_application/activescenes/scene_physics.h @@ -60,6 +60,7 @@ struct ACtxShapeSpawner osp::Session setup_physics( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& scene, osp::Session const& commonScene); /** @@ -78,6 +79,7 @@ osp::Session setup_shape_spawn( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, osp::Session const& scene, + osp::Session const& commonScene, osp::Session const& physics, osp::active::MaterialId materialId); diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 82f2b11d..ae51bc40 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -91,7 +91,7 @@ Session setup_magnum( Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_MAGNUM); auto const tgMgn = out.create_pipelines(rBuilder); - out.m_cleanup = tgMgn.cleanup.tpl(Working); + out.m_cleanup = tgMgn.cleanup.tpl(Run); // Order-dependent; ActiveApplication construction starts OpenGL context, needed by RenderGL /* unused */ top_emplace(topData, idActiveApp, args, rUserInput); @@ -101,7 +101,7 @@ Session setup_magnum( rBuilder.task() .name ("Clean up Magnum renderer") - .run_on ({tgMgn.cleanup(Working)}) + .run_on ({tgMgn.cleanup(Run)}) .push_to (out.m_tasks) .args ({ idResources, idRenderGl}) .func([] (Resources& rResources, RenderGL& rRenderGl) noexcept @@ -146,9 +146,9 @@ Session setup_scene_renderer( rBuilder.task() .name ("Resize Scene Render containers to fit drawable entities") - .run_on ({tgSR.drawTransforms(Resize)}) - .conditions ({tgCS.drawEntResized(Working)}) - .sync_with ({tgCS.drawEnt(New), tgSR.scnRender(New), tgSR.entMesh(New)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.drawEntResized(Write)}) + .sync_with ({tgSR.entMesh(New), tgSR.scnRender(New)}) .push_to (out.m_tasks) .args ({ idDrawing, idScnRender}) .func([] (ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept @@ -161,7 +161,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Compile Resource Meshes to GL") - .run_on ({tgCS.meshResDirty(Working)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.meshResDirty(Run)}) .sync_with ({tgCS.mesh(Use), tgMgn.meshGL(New)}) .push_to (out.m_tasks) .args ({ idDrawingRes, idResources, idRenderGl }) @@ -172,7 +173,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Compile Resource Textures to GL") - .run_on ({tgCS.textureResDirty(Working)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.textureResDirty(Run)}) .sync_with ({tgCS.texture(Use), tgMgn.textureGL(New)}) .push_to (out.m_tasks) .args ({ idDrawingRes, idResources, idRenderGl }) @@ -183,7 +185,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Assign GL textures to entities with scene textures") - .run_on ({tgCS.entTextureDirty(Use_)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.entTextureDirty(Use_)}) .sync_with ({tgCS.texture(Use), tgMgn.textureGL(Use), tgMgn.entTextureGL(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) @@ -194,7 +197,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Assign GL meshes to entities with scene meshes") - .run_on ({tgCS.entMeshDirty(Use_)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.entMeshDirty(Use_)}) .sync_with ({tgCS.mesh(Use), tgMgn.meshGL(Use), tgMgn.entMeshGL(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) @@ -205,7 +209,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Bind and display off-screen FBO") - .run_on ({tgWin.display(Working)}) + .run_on ({tgWin.display(Run)}) .sync_with ({tgSR.fboRender(Bind)}) .triggers ({tgSR.fboRender(Draw), tgSR.fboRender(Unbind)}) .push_to (out.m_tasks) @@ -230,7 +234,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Calculate draw transforms") - .run_on ({tgCS.transform(Use)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.transform(Use)}) .sync_with ({tgCS.hierarchy(Use), tgCS.activeEnt(Use), tgSR.drawTransforms(Modify_), tgCS.drawEnt(Use)}) .triggers ({tgSR.drawTransforms(Use_)}) .push_to (out.m_tasks) @@ -287,6 +292,7 @@ Session setup_shader_visualizer( TopTaskBuilder& rBuilder, ArrayView const topData, Session const& magnum, + Session const& scene, Session const& commonScene, Session const& scnRender, MaterialId const materialId) @@ -294,6 +300,7 @@ Session setup_shader_visualizer( OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(scnRender, TESTAPP_DATA_COMMON_RENDERER); OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); + auto const tgScn = scene .get_pipelines(); auto const tgCS = commonScene .get_pipelines(); auto const tgSR = scnRender .get_pipelines(); auto const tgMgn = magnum .get_pipelines(); @@ -320,7 +327,8 @@ Session setup_shader_visualizer( rBuilder.task() .name ("Sync MeshVisualizer shader entities") - .run_on ({tgCS.materialDirty(Use_)}) + .run_on ({tgScn.updDraw(Run)}) + .conditions ({tgCS.materialDirty(Use_)}) .sync_with ({tgSR.groupEnts(Use), tgSR.group(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idGroupFwd, idDrawShVisual}) diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index a5085059..dbc8f84d 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -65,6 +65,7 @@ osp::Session setup_shader_visualizer( osp::ArrayView topData, osp::Session const& magnum, osp::Session const& scene, + osp::Session const& commonScene, osp::Session const& scnRender, osp::active::MaterialId materialId = lgrn::id_null()); diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 71aa4e65..e1b30de6 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -64,8 +64,7 @@ void randomized_singlethreaded_execute(Tasks const& tasks, TaskGraph const& grap if (runTasksLeft != 0) { TaskId const randomTask = rExec.tasksQueuedRun.at(rRand() % runTasksLeft); - bool const canRun = conditions_satisfied(tasks, graph, rExec, randomTask); - TriggerOut_t const status = canRun ? runTask(randomTask) : gc_triggerNone; + TriggerOut_t const status = runTask(randomTask); complete_task(tasks, graph, rExec, randomTask, status); } @@ -78,7 +77,7 @@ void randomized_singlethreaded_execute(Tasks const& tasks, TaskGraph const& grap namespace test_a { -enum class Stages { Clear, Fill, Use }; +enum class Stages { Fill, Use, Clear }; struct Pipelines { @@ -120,8 +119,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) for (int i = 0; i < sc_pusherTaskCount; ++i) { builder.task() - .run_on ({pl.vec(Fill)}) - .triggers({pl.vec(Use), pl.vec(Clear)}) + .run_on (pl.vec(Fill)) .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t { rOut.push_back(in); @@ -131,7 +129,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) // Use vector builder.task() - .run_on({pl.vec(Use)}) + .run_on(pl.vec(Use)) .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t { int const sum = std::accumulate(rOut.begin(), rOut.end(), 0); @@ -167,7 +165,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) { input = 1 + randGen() % 30; - exec_trigger(exec, pl.vec(Fill)); + exec_run(exec, pl.vec); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output, &checksRun] (TaskId const task) -> TriggerOut_t @@ -179,7 +177,6 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) ASSERT_EQ(checksRun, sc_repetitions); } - //----------------------------------------------------------------------------- namespace test_b @@ -203,7 +200,6 @@ struct Pipelines } // namespace test_gameworld - // Test that features a looping 'normal' pipeline and an 'optional' pipeline that has a 50% chance of running TEST(Tasks, BasicSingleThreadedTriggers) { @@ -308,139 +304,6 @@ TEST(Tasks, BasicSingleThreadedTriggers) } -namespace test_c -{ - -struct TestState -{ - int checks { 0 }; - bool normalFlag { false }; - bool optionalFlagExpect { false }; - bool optionalFlag { false }; -}; - - -enum class StgRun { Wait, Run }; -enum class StgGuide { Clear, Schedule, Write, Read }; - -struct Pipelines -{ - osp::PipelineDef run; - osp::PipelineDef normal; - osp::PipelineDef optional; -}; - -} // namespace test_gameworld - -// Test that features a looping 'normal' pipeline and an 'optional' pipeline that has a 50% chance of running -TEST(Tasks, BasicSingleThreadedCondition) -{ - using namespace test_c; - using enum StgRun; - using enum StgGuide; - - using BasicTraits_t = BasicBuilderTraits; - using Builder_t = BasicTraits_t::Builder; - using TaskFuncVec_t = BasicTraits_t::FuncVec_t; - - constexpr int sc_taskRuns = 128; - std::mt19937 randGen(69); - - Tasks tasks; - TaskEdges edges; - TaskFuncVec_t functions; - Builder_t builder{tasks, edges, functions}; - - auto const pl = builder.create_pipelines(); - - // These tasks run in a loop, triggering each other to run continuously - - builder.task() - .run_on ({pl.run(Run)}) - .sync_with ({pl.optional(Schedule)}) - .triggers ({pl.optional(Write)}) - .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t - { - if (rRand() % 2 == 0) - { - return gc_triggerNone; - } - else - { - rState.optionalFlagExpect = true; - return gc_triggerAll; - } - }); - - builder.task() - .run_on ({pl.run(Run)}) - .sync_with ({pl.normal(Write)}) - .triggers ({pl.normal(Read)}) - .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t - { - rState.normalFlag = true; - return gc_triggerAll; - }); - - builder.task() - .run_on ({pl.run(Run)}) - .conditions ({pl.optional(Write)}) - .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t - { - rState.optionalFlag = true; - return gc_triggerNone; - }); - - - builder.task() - .run_on ({pl.run(Run)}) - .sync_with ({pl.optional(Read), pl.normal(Read)}) - .triggers ({pl.run(Run)}) - .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t - { - EXPECT_TRUE(rState.normalFlag); - EXPECT_EQ(rState.optionalFlagExpect, rState.optionalFlag); - return gc_triggerAll; - }); - - builder.task() - .run_on ({pl.run(Run)}) - .sync_with ({pl.optional(Clear), pl.normal(Clear)}) - .func ( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t - { - ++ rState.checks; - rState.normalFlag = false; - rState.optionalFlagExpect = false; - rState.optionalFlag = false; - return gc_triggerNone; - }); - - - TaskGraph const graph = make_exec_graph(tasks, {&edges}); - - // Execute - - ExecContext exec; - exec_resize(tasks, graph, exec); - - TestState world; - - exec_trigger(exec, pl.run(Run)); - enqueue_dirty(tasks, graph, exec); - - randomized_singlethreaded_execute( - tasks, graph, exec, randGen, sc_taskRuns, - [&functions, &world, &randGen] (TaskId const task) -> TriggerOut_t - { - return functions[task](world, randGen); - }); - - // Assure that the tasks above actually ran, and didn't just skip everything - // Max of 5 tasks run each loop - ASSERT_GT(world.checks, sc_taskRuns / 5); - -} - //----------------------------------------------------------------------------- namespace test_gameworld @@ -561,8 +424,10 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Enqueue initial tasks // This roughly indicates "Time has changed" and "Render requested" - exec_trigger(exec, pl.time(StgSimple::Use)); - exec_trigger(exec, pl.render(StgRender::Render)); + exec_run(exec, pl.time); + exec_run(exec, pl.forces); + exec_run(exec, pl.positions); + exec_run(exec, pl.render); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute( @@ -580,7 +445,3 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // TODO: Multi-threaded test with limits. Actual multithreading isn't needed; // as long as task_start/finish are called at the right times - - - - From 435c3b3c0a1d9121b7b3b0c3ca2e449d5d0910ea Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 23 Jul 2023 17:06:53 -0700 Subject: [PATCH 24/35] Implement optional pipeline stages --- src/osp/tasks/builder.h | 6 + src/osp/tasks/execute.cpp | 249 ++++++++++++++++++++++++++------------ src/osp/tasks/execute.h | 17 +-- src/osp/tasks/tasks.cpp | 117 +++++++++--------- src/osp/tasks/tasks.h | 40 +++--- src/osp/tasks/worker.h | 15 +-- test/tasks/main.cpp | 154 +++++++++++++---------- 7 files changed, 368 insertions(+), 230 deletions(-) diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 0d5fed56..597f7e13 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -75,6 +75,12 @@ struct TaskBuilderBase m_rTasks.m_pipelineIds.create(pipelines.begin(), pipelines.end()); + std::size_t const capacity = m_rTasks.m_pipelineIds.capacity(); + + m_rTasks.m_pipelineInfo .resize(capacity); + m_rTasks.m_pipelineControl.resize(capacity); + m_rTasks.m_pipelineParents.resize(capacity, lgrn::id_null()); + TGT_STRUCT_T out; auto const members = Corrade::Containers::staticArrayView(reinterpret_cast(&out)); diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 2c70a008..9cfa6b94 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -46,7 +46,7 @@ void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) exec_resize(tasks, rOut); } -void exec_run(ExecContext &rExec, PipelineId const pipeline) +void pipeline_run(ExecContext &rExec, PipelineId const pipeline) { rExec.plRequestRun.set(std::size_t(pipeline)); rExec.hasRequestRun = true; @@ -63,12 +63,24 @@ static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) static constexpr bool pipeline_can_advance(TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept { - int const tasksQueued = rExecPl.tasksQueuedBlocked + rExecPl.tasksQueuedRun; + return rExecPl.ownStageReqTasksLeft == 0 // Tasks required by stage are done + && rExecPl.tasksReqOwnStageLeft == 0 // Not required by any tasks + && (rExecPl.tasksQueuedBlocked+rExecPl.tasksQueuedRun) == 0; // Tasks done - return tasksQueued == 0 // Tasks done - && rExecPl.reqTasksLeft == 0 // Tasks required by stage are done - && rExecPl.reqByTaskLeft == 0; // Not required by any tasks +} + +static inline void pipeline_try_advance(TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) +{ + if (pipeline_can_advance(graph, rExec, rExecPl)) + { + rExec.plAdvance.set(std::size_t(pipeline)); + rExec.hasPlAdvance = true; + } +}; +static inline bool stage_is_cancelled(Tasks const& tasks, ExecContext const &rExec, ExecPipeline &rExecPl, PipelineId const pipeline, StageId const stage) +{ + return rExecPl.cancelOptionals && tasks.m_pipelineControl[pipeline].optionalStages.test(std::size_t(stage)); } static void pipeline_advance_stage(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) @@ -84,23 +96,24 @@ static void pipeline_advance_stage(TaskGraph const& graph, ExecContext &rExec, P auto const nextStage = StageId( justStarting ? 0 : (int(rExecPl.stage)+1) ); - if (nextStage == StageId(stageCount)) + if (nextStage != StageId(stageCount)) + { + rExecPl.stage = nextStage; + rExecPl.tasksQueued = false; + } + else { // Next stage is 1 past the last stage. Finished running - rExecPl.stage = lgrn::id_null(); - rExecPl.running = false; - return; + rExecPl.stage = lgrn::id_null(); + rExecPl.running = false; } - rExecPl.stage = nextStage; - rExecPl.tasksQueued = false; - // asserted by pipeline_can_advance: - // * rExecPl.reqTasksLeft == 0; - // * rExecPl.reqByTaskLeft == 0; + // * rExecPl.ownStageReqTasksLeft == 0; + // * rExecPl.tasksReqOwnStageLeft == 0; } -static void pipeline_advance_reqs(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { ExecPipeline &rExecPl = rExec.plData[pipeline]; @@ -116,14 +129,14 @@ static void pipeline_advance_reqs(TaskGraph const& graph, ExecContext &rExec, Pi auto const revTaskReqStageView = ArrayView(fanout_view(graph.anystgToFirstRevTaskreqstg, graph.revTaskreqstgToTask, anystg)); - // Number of tasks that require nextStage. This is decremented only when tasks finish - rExecPl.reqByTaskLeft = revTaskReqStageView.size(); + // Number of tasks that require this stage. This is decremented when required tasks finish + rExecPl.tasksReqOwnStageLeft = revTaskReqStageView.size(); - // Unblock tasks that are alredy queued for (TaskId const task : revTaskReqStageView) { if (rExec.tasksQueuedBlocked.contains(task)) { + // Unblock tasks that are alredy queued BlockedTask &rBlocked = rExec.tasksQueuedBlocked.get(task); -- rBlocked.reqStagesLeft; if (rBlocked.reqStagesLeft == 0) @@ -136,6 +149,12 @@ static void pipeline_advance_reqs(TaskGraph const& graph, ExecContext &rExec, Pi rExec.tasksQueuedBlocked.erase(task); } } + else if (auto const [reqPipeline, reqStage] = tasks.m_taskRunOn[task]; + stage_is_cancelled(tasks, rExec, rExec.plData[reqPipeline], reqPipeline, reqStage)) + { + // Task is cancelled + -- rExecPl.tasksReqOwnStageLeft; + } } // Evaluate Stage-requires-Tasks @@ -143,19 +162,23 @@ static void pipeline_advance_reqs(TaskGraph const& graph, ExecContext &rExec, Pi auto const stgreqtaskView = ArrayView(fanout_view(graph.anystgToFirstStgreqtask, graph.stgreqtaskData, anystg)); - rExecPl.reqTasksLeft = stgreqtaskView.size(); + rExecPl.ownStageReqTasksLeft = stgreqtaskView.size(); - // Decrement reqTasksLeft, as some of these tasks might already be complete + // Decrement ownStageReqTasksLeft, as some of these tasks might already be complete for (StageRequiresTask const& stgreqtask : stgreqtaskView) { ExecPipeline &rReqTaskExecPl = rExec.plData[stgreqtask.reqPipeline]; - bool const reqTaskDone = [&rReqTaskExecPl, &stgreqtask, &rExec] () noexcept -> bool + bool const reqTaskDone = [&tasks, &rReqTaskExecPl, &stgreqtask, &rExec] () noexcept -> bool { if ( ! rReqTaskExecPl.running ) { return true; // Not running, which means the whole pipeline finished already } + else if (stage_is_cancelled(tasks, rExec, rReqTaskExecPl, stgreqtask.reqPipeline, stgreqtask.reqStage)) + { + return true; // Stage cancelled. Required task is considered finish and will never run + } else if (int(rReqTaskExecPl.stage) < int(stgreqtask.reqStage)) { return false; // Not yet reached required stage. Required task didn't run yet @@ -181,12 +204,12 @@ static void pipeline_advance_reqs(TaskGraph const& graph, ExecContext &rExec, Pi if (reqTaskDone) { - -- rExecPl.reqTasksLeft; + -- rExecPl.ownStageReqTasksLeft; } } } -static void pipeline_advance_run(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { ExecPipeline &rExecPl = rExec.plData[pipeline]; @@ -195,46 +218,53 @@ static void pipeline_advance_run(TaskGraph const& graph, ExecContext &rExec, Pip return; } - AnyStageId const anystg = anystg_from(graph, pipeline, rExecPl.stage); - - auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; + bool const stageCancelled = rExecPl.cancelOptionals && tasks.m_pipelineControl[pipeline].optionalStages.test(std::size_t(rExecPl.stage)); + bool noTasksRun = true; - for (TaskId task : runTasks) + if ( ! stageCancelled ) { - LGRN_ASSERTM( ! rExec.tasksQueuedBlocked.contains(task), "Impossible to queue a task that's already queued"); - LGRN_ASSERTM( ! rExec.tasksQueuedRun .contains(task), "Impossible to queue a task that's already queued"); + auto const anystg = anystg_from(graph, pipeline, rExecPl.stage); + auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; - // Evaluate Task-requires-Stages - // Some requirements may already be satisfied - auto const taskreqstageView = ArrayView(fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)); - int reqStagesLeft = taskreqstageView.size(); + noTasksRun = runTasks.size() == 0; - for (TaskRequiresStage const& req : taskreqstageView) + for (TaskId task : runTasks) { - ExecPipeline const &rReqPlData = rExec.plData[req.reqPipeline]; + LGRN_ASSERTM( ! rExec.tasksQueuedBlocked.contains(task), "Impossible to queue a task that's already queued"); + LGRN_ASSERTM( ! rExec.tasksQueuedRun .contains(task), "Impossible to queue a task that's already queued"); - if (rReqPlData.stage == req.reqStage) + // Evaluate Task-requires-Stages + // Some requirements may already be satisfied + auto const taskreqstageView = ArrayView(fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)); + int reqStagesLeft = taskreqstageView.size(); + + for (TaskRequiresStage const& req : taskreqstageView) { - reqStagesLeft --; + ExecPipeline const &rReqPlData = rExec.plData[req.reqPipeline]; + + if (rReqPlData.stage == req.reqStage) + { + reqStagesLeft --; + } } - } - if (reqStagesLeft == 0) - { - // Task can run right away - rExec.tasksQueuedRun.emplace(task); - ++ rExecPl.tasksQueuedRun; - } - else - { - rExec.tasksQueuedBlocked.emplace(task, BlockedTask{reqStagesLeft, pipeline}); - ++ rExecPl.tasksQueuedBlocked; + if (reqStagesLeft == 0) + { + // Task can run right away + rExec.tasksQueuedRun.emplace(task); + ++ rExecPl.tasksQueuedRun; + } + else + { + rExec.tasksQueuedBlocked.emplace(task, BlockedTask{reqStagesLeft, pipeline}); + ++ rExecPl.tasksQueuedBlocked; + } } } rExecPl.tasksQueued = true; - if (runTasks.size() == 0 && pipeline_can_advance(graph, rExec, rExecPl)) + if (noTasksRun && pipeline_can_advance(graph, rExec, rExecPl)) { // No tasks to run. RunTasks are responsible for setting this pipeline dirty once they're // all done. If there is none, then this pipeline may get stuck if nothing sets it dirty, @@ -245,45 +275,110 @@ static void pipeline_advance_run(TaskGraph const& graph, ExecContext &rExec, Pip } -static void run_pipeline_recurse(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +static void run_pipeline_recurse(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { ExecPipeline &rExecPl = rExec.plData[pipeline]; if (fanout_size(graph.pipelineToFirstAnystg, pipeline) != 0) { - rExecPl.running = true; + rExecPl.running = true; + rExecPl.doLoop = tasks.m_pipelineControl[pipeline].loops; + rExecPl.cancelOptionals = false; - if (rExecPl.reqTasksLeft == 0) + if (rExecPl.ownStageReqTasksLeft == 0) { rExec.plAdvance.set(std::size_t(pipeline)); rExec.hasPlAdvance = true; } } - auto const subscribers = ArrayView{fanout_view(graph.pipelineToFirstSub, graph.subToPipeline, pipeline)}; + auto const children = ArrayView{fanout_view(graph.pipelineToFirstChild, graph.childPlToParent, pipeline)}; - for (PipelineId const plSub : subscribers) + for (PipelineId const plSub : children) { - run_pipeline_recurse(graph, rExec, PipelineId(plSub)); + run_pipeline_recurse(tasks, graph, rExec, PipelineId(plSub)); } } -void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept +void pipeline_cancel_optionals(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) { + if (rExecPl.cancelOptionals) + { + return; // already cancelled + } - exec_log(rExec, ExecContext::EnqueueStart{}); + StageBits_t const optionalStages = tasks.m_pipelineControl[pipeline].optionalStages; + int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); - [[maybe_unused]] std::size_t const queuedTasks = rExec.tasksQueuedRun.size() + rExec.tasksQueuedBlocked.size(); + auto anystgInt = uint32_t(anystg_from(graph, pipeline, rExecPl.stage)); - LGRN_ASSERTM( ! ( queuedTasks!=0 && rExec.plRequestRun.count()!=0 ), - "Running new pipelines while already running is not yet supported ROFL!"); + // For each cancelled stage + for (auto stgInt = int(rExecPl.stage); stgInt < stageCount; ++stgInt, ++anystgInt) + { + if ( ! optionalStages.test(stgInt) ) + { + continue; + } + + auto const anystg = AnyStageId(anystgInt); + + auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; + + for (TaskId task : runTasks) + { + // Stages depend on this RunTask (reverse Stage-requires-Task) + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) + { + PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); + ExecPipeline &rReqExecPl = rExec.plData[reqPl]; + + if (rReqExecPl.stage == reqStg) + { + LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); + -- rReqExecPl.ownStageReqTasksLeft; + pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); + } + } + + // RunTask depends on stages (Task-requires-Stage) + for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) + { + ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; + + if (rReqExecPl.stage == req.reqStage) + { + LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); + -- rReqExecPl.tasksReqOwnStageLeft; + pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); + } + } + } + } + + rExecPl.cancelOptionals = true; +} + +void pipeline_cancel_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) +{ + +} + +void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept +{ + exec_log(rExec, ExecContext::EnqueueStart{}); if (rExec.hasRequestRun) { + for (ExecPipeline const& rExecPl : rExec.plData) + { + LGRN_ASSERTM( ! rExecPl.running, "Running new pipelines while already running is not yet supported ROFL!"); + } + for (PipelineInt const plInt : rExec.plRequestRun.ones()) { auto const pipeline = PipelineId(plInt); - run_pipeline_recurse(graph, rExec, pipeline); + run_pipeline_recurse(tasks, graph, rExec, pipeline); } rExec.plRequestRun.reset(); rExec.hasRequestRun = false; @@ -303,12 +398,12 @@ void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe for (PipelineInt const plInt : rExec.plAdvance.ones()) { - pipeline_advance_reqs(graph, rExec, PipelineId(plInt)); + pipeline_advance_reqs(tasks, graph, rExec, PipelineId(plInt)); } for (PipelineInt const plInt : rExec.plAdvance.ones()) { - pipeline_advance_run(graph, rExec, PipelineId(plInt)); + pipeline_advance_run(tasks, graph, rExec, PipelineId(plInt)); } std::copy(rExec.plAdvanceNext.ints().begin(), @@ -320,17 +415,8 @@ void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe exec_log(rExec, ExecContext::EnqueueEnd{}); } -void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TriggerOut_t dirty) noexcept +void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TaskActions actions) noexcept { - auto const try_advance = [&graph, &rExec] (PipelineId const pipeline, ExecPipeline &rExecPl) - { - if (pipeline_can_advance(graph, rExec, rExecPl)) - { - rExec.plAdvance.set(std::size_t(pipeline)); - rExec.hasPlAdvance = true; - } - }; - LGRN_ASSERT(rExec.tasksQueuedRun.contains(task)); rExec.tasksQueuedRun.erase(task); @@ -341,7 +427,7 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe -- rExecPl.tasksQueuedRun; - try_advance(pipeline, rExecPl); + pipeline_try_advance(graph, rExec, rExecPl, pipeline); // Handle stages requiring this task for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) @@ -352,9 +438,9 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe if (rReqExecPl.stage == reqStg) { - -- rReqExecPl.reqTasksLeft; + -- rReqExecPl.ownStageReqTasksLeft; - try_advance(reqPl, rReqExecPl); + pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); } else { @@ -373,9 +459,16 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe "Task-requires-Stage means this task should have not run unless the stage is selected", int(task), int(rReqExecPl.stage), int(req.reqStage)); - -- rReqExecPl.reqByTaskLeft; + -- rReqExecPl.tasksReqOwnStageLeft; + + pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); + } - try_advance(req.reqPipeline, rReqExecPl); + // Handle actions + + if (actions & TaskAction::CancelOptionalStages) + { + pipeline_cancel_optionals(tasks, graph, rExec, rExecPl, pipeline); } } diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index d1a589ed..fd82810c 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -43,13 +43,14 @@ struct ExecPipeline int tasksQueuedRun {0}; int tasksQueuedBlocked {0}; - int reqByTaskLeft {0}; - int reqTasksLeft {0}; + int tasksReqOwnStageLeft{0}; + int ownStageReqTasksLeft{0}; StageId stage { lgrn::id_null() }; bool tasksQueued { false }; bool running { false }; - bool mightLoop { false }; + bool doLoop { false }; + bool cancelOptionals { false }; }; struct BlockedTask @@ -145,13 +146,15 @@ void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); void exec_resize(Tasks const& tasks, ExecContext &rOut); -void exec_run(ExecContext &rExec, PipelineId pipeline); +void pipeline_run(ExecContext &rExec, PipelineId pipeline); -void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; +void pipeline_cancel_optionals(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline); + +void pipeline_cancel_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline); -bool conditions_satisfied(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId task) noexcept; +void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; -void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId task, TriggerOut_t dirty) noexcept; +void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId task, TaskActions actions) noexcept; diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index 830dbc20..a1eef38d 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -46,7 +46,7 @@ struct PipelineCounts { std::array stageCounts; - uint16_t publishTo {0}; + uint16_t children {0}; uint8_t stages {0}; }; @@ -67,7 +67,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView std::size_t totalTasksReqStage = 0; std::size_t totalStageReqTasks = 0; std::size_t totalRunTasks = 0; - std::size_t totalSubscriptions = 0; + std::size_t totalChildren = 0; std::size_t totalStages = 0; // 1. Count total number of stages @@ -104,7 +104,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView totalStages += plCount.stages; } - // 2. Count TaskRequiresStages and StageRequiresTasks and Subscriptions + // 2. Count TaskRequiresStages and StageRequiresTasks for (TaskEdges const* pEdges : data) { @@ -125,16 +125,22 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView } totalTasksReqStage += pEdges->m_syncWith.size(); totalStageReqTasks += pEdges->m_syncWith.size(); + } + + // 3. Count pipeline children + + for (PipelineInt const pipelineInt : tasks.m_pipelineIds.bitview().zeros()) + { + PipelineId const parent = tasks.m_pipelineParents[PipelineId(pipelineInt)]; - for (auto const [publisher, subscriber] : pEdges->m_subscriptions) + if (parent != lgrn::id_null()) { - ++ plCounts[publisher].publishTo; + ++ plCounts[parent].children; + ++ totalChildren; } - totalSubscriptions += pEdges->m_subscriptions.size(); } - - // 3. Allocate + // 4. Allocate // The +1 is needed for 1-to-many connections to store the total number of other elements they // index. This also simplifies logic in fanout_view(...) @@ -143,8 +149,8 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView out.anystgToPipeline .resize(totalStages+1, lgrn::id_null()); out.anystgToFirstRuntask .resize(totalStages+1, lgrn::id_null()); out.runtaskToTask .resize(totalRunTasks, lgrn::id_null()); - out.pipelineToFirstSub .resize(maxPipelines+1, lgrn::id_null()); - out.subToPipeline .resize(totalSubscriptions, lgrn::id_null()); + out.pipelineToFirstChild .resize(maxPipelines+1, lgrn::id_null()); + out.childPlToParent .resize(totalChildren, lgrn::id_null()); out.anystgToFirstStgreqtask .resize(totalStages+1, lgrn::id_null()); out.stgreqtaskData .resize(totalStageReqTasks, {}); out.taskToFirstRevStgreqtask .resize(maxTasks+1, lgrn::id_null()); @@ -154,7 +160,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView out.anystgToFirstRevTaskreqstg .resize(totalStages+1, lgrn::id_null()); out.revTaskreqstgToTask .resize(totalTasksReqStage, lgrn::id_null()); - // 4. Calculate one-to-many partitions + // 5. Calculate one-to-many partitions fanout_partition( out.pipelineToFirstAnystg, @@ -176,9 +182,9 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView [] (AnyStageId, RunTaskId) { }); fanout_partition( - out.pipelineToFirstSub, - [&plCounts] (PipelineId pl) { return plCounts[pl].publishTo; }, - [&out] (PipelineId, PipelineSubId) { }); + out.pipelineToFirstChild, + [&plCounts] (PipelineId pl) { return plCounts[pl].children; }, + [&out] (PipelineId, ChildPipelineId) { }); fanout_partition( out.anystgToFirstStgreqtask, @@ -216,7 +222,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView }, [&out] (AnyStageId, ReverseTaskReqStageId) { }); - // 5. Push + // 6. Push for (TaskInt const taskInt : tasks.m_taskIds.bitview().zeros()) { @@ -231,55 +237,56 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView -- rStageCounts.runTasks; } - auto add_stagereqtask = [&plCounts, &taskCounts, &totalStageReqTasks, &out] - (PipelineId const pl, StageId const stg, TaskId const task) - { - AnyStageId const anystg = anystg_from(out, pl, stg); - StageCounts &rStageCounts = plCounts[pl].stageCounts[std::size_t(stg)]; - TaskCounts &rTaskCounts = taskCounts[task]; - - StageReqTaskId const stgReqTaskId = id_from_count(out.anystgToFirstStgreqtask, anystg, rStageCounts.requiresTasks); - ReverseStageReqTaskId const revStgReqTaskId = id_from_count(out.taskToFirstRevStgreqtask, task, rTaskCounts.requiredByStages); - - StageRequiresTask &rStgReqTask = out.stgreqtaskData[stgReqTaskId]; - rStgReqTask.reqTask = task; - rStgReqTask.reqPipeline = pl; - rStgReqTask.reqStage = stg; - out.revStgreqtaskToStage[revStgReqTaskId] = anystg; - - -- rStageCounts.requiresTasks; - -- rTaskCounts.requiredByStages; - -- totalStageReqTasks; - }; - auto add_taskreqstage = [&plCounts, &taskCounts, &totalTasksReqStage, &out] (TaskId const task, PipelineId const pl, StageId const stg) + for (TaskEdges const* pEdges : data) { - AnyStageId const anystg = anystg_from(out, pl, stg); - StageCounts &rStageCounts = plCounts[pl].stageCounts[std::size_t(stg)]; - TaskCounts &rTaskCounts = taskCounts[task]; - TaskReqStageId const taskReqStgId = id_from_count(out.taskToFirstTaskreqstg, task, rTaskCounts.requiresStages); - ReverseTaskReqStageId const revTaskReqStgId = id_from_count(out.anystgToFirstRevTaskreqstg, anystg, rStageCounts.requiredByTasks); + for (auto const [task, pipeline, stage] : pEdges->m_syncWith) + { + AnyStageId const anystg = anystg_from(out, pipeline, stage); + StageCounts &rStageCounts = plCounts[pipeline].stageCounts[std::size_t(stage)]; + TaskCounts &rTaskCounts = taskCounts[task]; - TaskRequiresStage &rTaskReqStage = out.taskreqstgData[taskReqStgId]; - rTaskReqStage.reqStage = stg; - rTaskReqStage.reqPipeline = pl; - out.revTaskreqstgToTask[revTaskReqStgId] = task; + auto const [taskPipeline, taskStage] = tasks.m_taskRunOn[task]; - -- rTaskCounts.requiresStages; - -- rStageCounts.requiredByTasks; - -- totalTasksReqStage; - }; + // Add StageReqTask (pipeline, stage) requires task + StageReqTaskId const stgReqTaskId = id_from_count(out.anystgToFirstStgreqtask, anystg, rStageCounts.requiresTasks); + ReverseStageReqTaskId const revStgReqTaskId = id_from_count(out.taskToFirstRevStgreqtask, task, rTaskCounts.requiredByStages); - for (TaskEdges const* pEdges : data) - { + StageRequiresTask &rStgReqTask = out.stgreqtaskData[stgReqTaskId]; - for (auto const [task, pipeline, stage] : pEdges->m_syncWith) - { - add_stagereqtask(pipeline, stage, task); - add_taskreqstage(task, pipeline, stage); + // rTaskReqStage.ownStage set previously + rStgReqTask.reqTask = task; + rStgReqTask.reqPipeline = taskPipeline; + rStgReqTask.reqStage = taskStage; + out.revStgreqtaskToStage[revStgReqTaskId] = anystg; + + -- rStageCounts.requiresTasks; + -- rTaskCounts.requiredByStages; + -- totalStageReqTasks; + + // Add TaskReqStage task requires (pipeline, stage) + TaskReqStageId const taskReqStgId = id_from_count(out.taskToFirstTaskreqstg, task, rTaskCounts.requiresStages); + ReverseTaskReqStageId const revTaskReqStgId = id_from_count(out.anystgToFirstRevTaskreqstg, anystg, rStageCounts.requiredByTasks); + + TaskRequiresStage &rTaskReqStage = out.taskreqstgData[taskReqStgId]; + + // rTaskReqStage.ownTask set previously + rTaskReqStage.reqStage = stage; + rTaskReqStage.reqPipeline = pipeline; + out.revTaskreqstgToTask[revTaskReqStgId] = task; + + -- rTaskCounts.requiresStages; + -- rStageCounts.requiredByTasks; + -- totalTasksReqStage; } + } + + for (PipelineInt const pipelineInt : tasks.m_pipelineIds.bitview().zeros()) + { + // PipelineId const parent = tasks.m_pipelineParents[PipelineId(pipelineInt)]; + // TODO } [[maybe_unused]] auto const all_counts_zero = [&] () diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 4b11c6e3..709f8c62 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -35,13 +35,14 @@ #include #include #include +#include #include -#define OSP_DECLARE_STAGE_NAMES(type, ...) \ - inline osp::ArrayView stage_names([[maybe_unused]] type _) noexcept \ - { \ - static auto const arr = std::array{__VA_ARGS__}; \ - return osp::arrayView( arr.data(), arr.size() ); \ +#define OSP_DECLARE_STAGE_NAMES(type, ...) \ + inline osp::ArrayView stage_names([[maybe_unused]] type _) noexcept \ + { \ + static auto const arr = std::array{__VA_ARGS__}; \ + return osp::arrayView( arr.data(), arr.size() ); \ } namespace osp @@ -61,6 +62,8 @@ enum class PipelineId : PipelineInt { }; enum class StageId : StageInt { }; enum class SemaphoreId : SemaphoreInt { }; +//----------------------------------------------------------------------------- + struct PipelineInfo { using stage_type_family_t = entt::family; @@ -73,6 +76,12 @@ struct PipelineInfo stage_type_t stageType; }; +struct PipelineControl +{ + StageBits_t optionalStages; + bool loops; +}; + struct TplTaskPipelineStage { TaskId task; @@ -86,18 +95,14 @@ struct TplPipelineStage StageId stage; }; -struct PipelineSubscribe -{ - PipelineId publisher; - PipelineId subscriber; -}; - struct TplTaskSemaphore { TaskId task; SemaphoreId semaphore; }; +//----------------------------------------------------------------------------- + struct Tasks { lgrn::IdRegistryStl m_taskIds; @@ -107,6 +112,8 @@ struct Tasks KeyedVec m_semaLimits; KeyedVec m_pipelineInfo; + KeyedVec m_pipelineParents; + KeyedVec m_pipelineControl; KeyedVec m_taskRunOn; }; @@ -114,7 +121,6 @@ struct Tasks struct TaskEdges { std::vector m_syncWith; - std::vector m_subscriptions; std::vector m_semaphoreEdges; }; @@ -124,7 +130,7 @@ enum class AnyStageId : uint32_t { }; enum class RunTaskId : uint32_t { }; enum class RunStageId : uint32_t { }; -enum class PipelineSubId : uint32_t { }; +enum class ChildPipelineId : uint32_t { }; enum class StageReqTaskId : uint32_t { }; enum class ReverseStageReqTaskId : uint32_t { }; @@ -165,10 +171,10 @@ struct TaskGraph KeyedVec anystgToFirstRuntask; KeyedVec runtaskToTask; - // Each pipeline has multiple subscriber pipelines - // PipelineId --> PipelineSubId --> many PipelineId - KeyedVec pipelineToFirstSub; - KeyedVec subToPipeline; + // Each pipeline has multiple child pipelines + // PipelineId --> PipelineChildId --> many PipelineId + KeyedVec pipelineToFirstChild; + KeyedVec childPlToParent; // Each stage has multiple entrance requirements. // AnyStageId <--> many StageEnterReqId diff --git a/src/osp/tasks/worker.h b/src/osp/tasks/worker.h index 29950c58..074709af 100644 --- a/src/osp/tasks/worker.h +++ b/src/osp/tasks/worker.h @@ -25,17 +25,18 @@ #pragma once #include +#include namespace osp { -/** - * @brief Bitset returned by tasks to determine which specified (pipeline,stage) to trigger - */ -using TriggerOut_t = std::bitset<64>; - -constexpr TriggerOut_t gc_triggerAll {0xFFFFFFFFFFFFFFFFul}; -constexpr TriggerOut_t gc_triggerNone {0}; +enum class TaskAction +{ + CancelLoop = 1 << 0, + CancelOptionalStages = 1 << 1 +}; +using TaskActions = Corrade::Containers::EnumSet; +CORRADE_ENUMSET_OPERATORS(TaskActions) } // namespace osp diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index e1b30de6..4b2cc37d 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -64,7 +64,7 @@ void randomized_singlethreaded_execute(Tasks const& tasks, TaskGraph const& grap if (runTasksLeft != 0) { TaskId const randomTask = rExec.tasksQueuedRun.at(rRand() % runTasksLeft); - TriggerOut_t const status = runTask(randomTask); + TaskActions const status = runTask(randomTask); complete_task(tasks, graph, rExec, randomTask, status); } @@ -89,6 +89,7 @@ struct Pipelines // Test pipeline consisting of parallel tasks TEST(Tasks, BasicSingleThreadedParallelTasks) { + return; using namespace test_a; using enum Stages; @@ -98,7 +99,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) // well-suited for this problem, as these per-thread vectors can all be represented with the // same TargetId. - using BasicTraits_t = BasicBuilderTraits&, int&)>; + using BasicTraits_t = BasicBuilderTraits&, int&)>; using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; @@ -120,31 +121,31 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) { builder.task() .run_on (pl.vec(Fill)) - .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t + .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TaskActions { rOut.push_back(in); - return osp::gc_triggerAll; + return {}; }); } // Use vector builder.task() .run_on(pl.vec(Use)) - .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t + .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TaskActions { int const sum = std::accumulate(rOut.begin(), rOut.end(), 0); EXPECT_EQ(sum, in * sc_pusherTaskCount); ++rChecksRun; - return osp::gc_triggerNone; + return {}; }); // Clear vector after use builder.task() .run_on({pl.vec(Clear)}) - .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TriggerOut_t + .func( [] (int const in, std::vector& rOut, int &rChecksRun) -> TaskActions { rOut.clear(); - return osp::gc_triggerNone; + return {}; }); // Step 2: Compile tasks into an execution graph @@ -165,10 +166,10 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) { input = 1 + randGen() % 30; - exec_run(exec, pl.vec); + pipeline_run(exec, pl.vec); enqueue_dirty(tasks, graph, exec); - randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output, &checksRun] (TaskId const task) -> TriggerOut_t + randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output, &checksRun] (TaskId const task) -> TaskActions { return functions[task](input, output, checksRun); }); @@ -185,9 +186,9 @@ namespace test_b struct TestState { int checks { 0 }; - bool normalFlag { false }; - bool optionalFlagExpect { false }; - bool optionalFlag { false }; + bool normalDone { false }; + bool expectOptionalDone { false }; + bool optionalDone { false }; }; enum class Stages { Schedule, Write, Read, Clear }; @@ -196,21 +197,24 @@ struct Pipelines { osp::PipelineDef normal; osp::PipelineDef optional; + + // Extra pipeline blocked by optional task to make the test case more difficult + osp::PipelineDef distraction; }; } // namespace test_gameworld // Test that features a looping 'normal' pipeline and an 'optional' pipeline that has a 50% chance of running -TEST(Tasks, BasicSingleThreadedTriggers) +TEST(Tasks, BasicSingleThreadedOptional) { using namespace test_b; using enum Stages; - using BasicTraits_t = BasicBuilderTraits; + using BasicTraits_t = BasicBuilderTraits; using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; - constexpr int sc_taskRuns = 128; + constexpr int sc_repetitions = 128; std::mt19937 randGen(69); Tasks tasks; @@ -220,65 +224,78 @@ TEST(Tasks, BasicSingleThreadedTriggers) auto const pl = builder.create_pipelines(); + tasks.m_pipelineControl[pl.optional].optionalStages.set(std::size_t(Write)); + // These tasks run in a loop, triggering each other to run continuously builder.task() - .run_on ({pl.normal(Schedule)}) - .triggers ({pl.normal(Write), pl.optional(Write)}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + .run_on ({pl.optional(Schedule)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { if (rRand() % 2 == 0) { - return 0b01; // trigger pl.normal(Write) only + rState.expectOptionalDone = true; + return {}; } else { - rState.optionalFlagExpect = true; - return 0b11; // trigger pl.normal(Write) and pl.optional(Write) + return TaskAction::CancelOptionalStages; } }); builder.task() .run_on ({pl.normal(Write)}) - .triggers ({pl.normal(Read), pl.normal(Clear)}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { - rState.normalFlag = true; - return gc_triggerAll; + rState.normalDone = true; + return {}; }); builder.task() .run_on ({pl.optional(Write)}) - .triggers ({pl.optional(Read), pl.optional(Clear)}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + .sync_with({pl.distraction(Read)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { - rState.optionalFlag = true; - return gc_triggerAll; + rState.optionalDone = true; + return {}; }); - builder.task() .run_on ({pl.normal(Read)}) .sync_with({pl.optional(Read)}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { - EXPECT_TRUE(rState.normalFlag); - EXPECT_EQ(rState.optionalFlagExpect, rState.optionalFlag); - return gc_triggerAll; + EXPECT_TRUE(rState.normalDone); + EXPECT_EQ(rState.expectOptionalDone, rState.optionalDone); + return {}; }); builder.task() .run_on ({pl.normal(Clear)}) - .triggers ({pl.normal(Schedule)}) - .func( [] (TestState& rState, std::mt19937 &rRand) -> TriggerOut_t + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { ++ rState.checks; - rState.normalFlag = false; - rState.optionalFlagExpect = false; - rState.optionalFlag = false; - return gc_triggerAll; + rState.normalDone = false; + rState.expectOptionalDone = false; + rState.optionalDone = false; + return {}; + }); + + builder.task() + .run_on ({pl.distraction(Write)}) + .func( [] (TestState&, std::mt19937&) -> TaskActions + { + return {}; + }); + + builder.task() + .run_on ({pl.distraction(Read)}) + .func( [] (TestState&, std::mt19937&) -> TaskActions + { + return {}; }); + TaskGraph const graph = make_exec_graph(tasks, {&edges}); // Execute @@ -288,19 +305,24 @@ TEST(Tasks, BasicSingleThreadedTriggers) TestState world; - exec_trigger(exec, pl.normal(Schedule)); - enqueue_dirty(tasks, graph, exec); - - randomized_singlethreaded_execute( - tasks, graph, exec, randGen, sc_taskRuns, - [&functions, &world, &randGen] (TaskId const task) -> TriggerOut_t + for (int i = 0; i < sc_repetitions; ++i) { - return functions[task](world, randGen); - }); + pipeline_run(exec, pl.normal); + pipeline_run(exec, pl.optional); + pipeline_run(exec, pl.distraction); + enqueue_dirty(tasks, graph, exec); + + randomized_singlethreaded_execute( + tasks, graph, exec, randGen, 10, + [&functions, &world, &randGen] (TaskId const task) -> TaskActions + { + return functions[task](world, randGen); + }); + } // Assure that the tasks above actually ran, and didn't just skip everything // Max of 5 tasks run each loop - ASSERT_GT(world.checks, sc_taskRuns / 5); + ASSERT_GT(world.checks, sc_repetitions / 5); } @@ -338,7 +360,7 @@ TEST(Tasks, BasicSingleThreadedGameWorld) using enum StgSimple; using enum StgRender; - using BasicTraits_t = BasicBuilderTraits; + using BasicTraits_t = BasicBuilderTraits; using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; @@ -358,30 +380,30 @@ TEST(Tasks, BasicSingleThreadedGameWorld) builder.task() .run_on ({pl.time(Use)}) .sync_with({pl.forces(Recalc)}) - .func( [] (World& rWorld) -> TriggerOut_t + .func( [] (World& rWorld) -> TaskActions { rWorld.m_forces += 42 * rWorld.m_deltaTimeIn; - return gc_triggerNone; + return {}; }); builder.task() .run_on ({pl.time(Use)}) .sync_with({pl.forces(Recalc)}) - .func([] (World& rWorld) -> TriggerOut_t + .func([] (World& rWorld) -> TaskActions { rWorld.m_forces += 1337 * rWorld.m_deltaTimeIn; - return gc_triggerNone; + return {}; }); // Main Physics update builder.task() .run_on ({pl.time(Use)}) .sync_with({pl.forces(Use), pl.positions(Recalc)}) - .func([] (World& rWorld) -> TriggerOut_t + .func([] (World& rWorld) -> TaskActions { EXPECT_EQ(rWorld.m_forces, 1337 + 42); rWorld.m_positions += rWorld.m_forces; rWorld.m_forces = 0; - return gc_triggerNone; + return {}; }); // Draw things moved by physics update. If 'updWorld' wasn't enqueued, then @@ -389,21 +411,21 @@ TEST(Tasks, BasicSingleThreadedGameWorld) builder.task() .run_on ({pl.render(Render)}) .sync_with({pl.positions(Use)}) - .func([] (World& rWorld) -> TriggerOut_t + .func([] (World& rWorld) -> TaskActions { EXPECT_EQ(rWorld.m_positions, 1337 + 42); rWorld.m_canvas.emplace("Physics Cube"); - return gc_triggerNone; + return {}; }); // Draw things unrelated to physics. This is allowed to be the first task // to run builder.task() .run_on ({pl.render(Render)}) - .func([] (World& rWorld) -> TriggerOut_t + .func([] (World& rWorld) -> TaskActions { rWorld.m_canvas.emplace("Terrain"); - return gc_triggerNone; + return {}; }); TaskGraph const graph = make_exec_graph(tasks, {&edges}); @@ -424,15 +446,15 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Enqueue initial tasks // This roughly indicates "Time has changed" and "Render requested" - exec_run(exec, pl.time); - exec_run(exec, pl.forces); - exec_run(exec, pl.positions); - exec_run(exec, pl.render); + pipeline_run(exec, pl.time); + pipeline_run(exec, pl.forces); + pipeline_run(exec, pl.positions); + pipeline_run(exec, pl.render); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute( tasks, graph, exec, randGen, 5, - [&functions, &world] (TaskId const task) -> TriggerOut_t + [&functions, &world] (TaskId const task) -> TaskActions { return functions[task](world); }); From 561eb96661cc31697eae60bceaa6d31fc51c53ba Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Wed, 26 Jul 2023 22:07:25 -0700 Subject: [PATCH 25/35] Remove optional Pipeline stages, Improve pipeline tree structure --- src/osp/Active/basic.h | 7 +- src/osp/tasks/builder.h | 106 +++++++-- src/osp/tasks/execute.cpp | 481 ++++++++++---------------------------- src/osp/tasks/execute.h | 7 +- src/osp/tasks/tasks.cpp | 102 ++++++-- src/osp/tasks/tasks.h | 21 +- src/osp/tasks/worker.h | 4 +- test/tasks/main.cpp | 110 ++++++++- 8 files changed, 407 insertions(+), 431 deletions(-) diff --git a/src/osp/Active/basic.h b/src/osp/Active/basic.h index 23a84b20..430d7e92 100644 --- a/src/osp/Active/basic.h +++ b/src/osp/Active/basic.h @@ -54,9 +54,10 @@ using TreePos_t = uint32_t; struct ACtxSceneGraph { - // Tree structure stored using an array of descendant count in parallel with - // identificaton (entities) - // A(B(C(D)), E(F(G(H,I)))) -> [A,B,C,D,E,F,G,H,I] and [8,2,1,0,4,3,2,0,0] + // N-ary tree structure represented as an array of descendant counts. Each node's subtree of + // descendants is positioned directly after it within the array. + // Example for tree structure "A( B(C(D)), E(F(G(H,I))) )" + // * Descendant Count array: [A:8, B:2, C:1, D:0, E:4, F:3, G:2, H:0, I:0] std::vector m_treeToEnt{lgrn::id_null()}; std::vector m_treeDescendants{std::initializer_list{0}}; diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 597f7e13..e0ba0ee0 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -40,15 +40,21 @@ namespace osp /** * @brief A convenient interface for setting up Tasks and required task data */ -template +template struct TaskBuilderBase { + using Builder_t = typename TRAITS_T::Builder_t; + using TaskRef_t = typename TRAITS_T::TaskRef_t; + + template + using PipelineRef_t = typename TRAITS_T::template PipelineRef; + constexpr TaskBuilderBase(Tasks &rTasks, TaskEdges &rEdges) noexcept : m_rTasks{rTasks} , m_rEdges{rEdges} { } - TASKREF_T task() + TaskRef_t task() { TaskId const taskId = m_rTasks.m_taskIds.create(); @@ -57,16 +63,25 @@ struct TaskBuilderBase return task(taskId); }; - constexpr TASKREF_T task(TaskId const taskId) noexcept + [[nodiscard]] constexpr TaskRef_t task(TaskId const taskId) noexcept { - return TASKREF_T{ + return TaskRef_t{ taskId, - static_cast(*this) + static_cast(*this) + }; + } + + template + [[nodiscard]] constexpr PipelineRef_t pipeline(PipelineDef pipelineDef) noexcept + { + return PipelineRef_t{ + pipelineDef.m_value, + static_cast(*this) }; } template - TGT_STRUCT_T create_pipelines() + [[nodiscard]] TGT_STRUCT_T create_pipelines() { static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineDefBlank_t) == 0); constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineDefBlank_t); @@ -107,10 +122,11 @@ struct TaskBuilderBase }; // class TaskBuilderBase -template +template struct TaskRefBase { - struct Empty {}; + using Builder_t = typename TRAITS_T::Builder_t; + using TaskRef_t = typename TRAITS_T::TaskRef_t; constexpr operator TaskId() noexcept { @@ -120,7 +136,7 @@ struct TaskRefBase constexpr Tasks & tasks() noexcept { return m_rBuilder.m_rTasks; } template - TASKREF_T& add_edges(std::vector& rContainer, RANGE_T const& add) + TaskRef_t& add_edges(std::vector& rContainer, RANGE_T const& add) { for (auto const [pipeline, stage] : add) { @@ -130,30 +146,58 @@ struct TaskRefBase .stage = stage }); } - return static_cast(*this); + return static_cast(*this); } - TASKREF_T& run_on(TplPipelineStage const tpl) noexcept + TaskRef_t& run_on(TplPipelineStage const tpl) noexcept { m_rBuilder.m_rTasks.m_taskRunOn.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rTasks.m_taskRunOn[m_taskId] = tpl; - return static_cast(*this); + return static_cast(*this); } - TASKREF_T& sync_with(ArrayView const specs) noexcept + TaskRef_t& sync_with(ArrayView const specs) noexcept { return add_edges(m_rBuilder.m_rEdges.m_syncWith, specs); } - TASKREF_T& sync_with(std::initializer_list specs) noexcept + TaskRef_t& sync_with(std::initializer_list specs) noexcept { return add_edges(m_rBuilder.m_rEdges.m_syncWith, specs); } - TaskId m_taskId; - TASKBUILDER_T & m_rBuilder; + Builder_t & m_rBuilder; + +}; // struct TaskRefBase + +template +struct PipelineRefBase +{ + using Builder_t = typename TRAITS_T::Builder_t; + using PipelineRef_t = typename TRAITS_T::template PipelineRef_t; + using TaskRef_t = typename TRAITS_T::TaskRef_t; + + constexpr operator PipelineId() noexcept + { + return m_pipelineId; + } + + PipelineRef_t& parent(PipelineId const parent) + { + m_rBuilder.m_rTasks.m_pipelineParents[m_pipelineId] = parent; + return static_cast(*this); + } + + PipelineRef_t& loops(bool const loop) + { + m_rBuilder.m_rTasks.m_pipelineControl[m_pipelineId].loops = loop; + return static_cast(*this); + } + + PipelineId m_pipelineId; + Builder_t & m_rBuilder; }; // struct TaskRefBase @@ -164,16 +208,26 @@ struct BasicBuilderTraits using FuncVec_t = KeyedVec; struct Builder; + struct TaskRef; - struct Ref : public TaskRefBase - { - Ref& func(FUNC_T && in); - }; + template + struct PipelineRef; + + using Builder_t = Builder; - struct Builder : public TaskBuilderBase + using TaskRef_t = TaskRef; + + template + using PipelineRef_t = PipelineRef; + + template + struct PipelineRef : public PipelineRefBase + { }; + + struct Builder : public TaskBuilderBase { Builder(Tasks& rTasks, TaskEdges& rEdges, FuncVec_t& rFuncs) - : TaskBuilderBase{ rTasks, rEdges } + : TaskBuilderBase{ rTasks, rEdges } , m_rFuncs{rFuncs} { } Builder(Builder const& copy) = delete; @@ -183,10 +237,16 @@ struct BasicBuilderTraits FuncVec_t & m_rFuncs; }; + + struct TaskRef : public TaskRefBase + { + TaskRef& func(FUNC_T && in); + }; + }; template -typename BasicBuilderTraits::Ref& BasicBuilderTraits::Ref::func(FUNC_T && in) +typename BasicBuilderTraits::TaskRef& BasicBuilderTraits::TaskRef::func(FUNC_T && in) { this->m_rBuilder.m_rFuncs.resize(this->m_rBuilder.m_rTasks.m_taskIds.capacity()); this->m_rBuilder.m_rFuncs[this->m_taskId] = std::move(in); diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 9cfa6b94..13be36d5 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -25,6 +25,8 @@ #include "execute.h" +#include + namespace osp { @@ -61,6 +63,11 @@ static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) } } +static bool pipeline_is_ancestor_descendant(Tasks const& tasks, TaskGraph const& graph, PipelineId const ancestor, PipelineId const descendant) +{ + //tasks. +} + static constexpr bool pipeline_can_advance(TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept { return rExecPl.ownStageReqTasksLeft == 0 // Tasks required by stage are done @@ -78,16 +85,13 @@ static inline void pipeline_try_advance(TaskGraph const& graph, ExecContext &rEx } }; -static inline bool stage_is_cancelled(Tasks const& tasks, ExecContext const &rExec, ExecPipeline &rExecPl, PipelineId const pipeline, StageId const stage) -{ - return rExecPl.cancelOptionals && tasks.m_pipelineControl[pipeline].optionalStages.test(std::size_t(stage)); -} - -static void pipeline_advance_stage(TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) { ExecPipeline &rExecPl = rExec.plData[pipeline]; LGRN_ASSERT(pipeline_can_advance(graph, rExec, rExecPl)); + // * rExecPl.ownStageReqTasksLeft == 0; + // * rExecPl.tasksReqOwnStageLeft == 0; int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); LGRN_ASSERTM(stageCount != 0, "Pipelines with 0 stages shouldn't be running"); @@ -96,21 +100,26 @@ static void pipeline_advance_stage(TaskGraph const& graph, ExecContext &rExec, P auto const nextStage = StageId( justStarting ? 0 : (int(rExecPl.stage)+1) ); + rExecPl.tasksQueueDone = false; + if (nextStage != StageId(stageCount)) { - rExecPl.stage = nextStage; - rExecPl.tasksQueued = false; + // Proceed to next stage since its valid + rExecPl.stage = nextStage; + } + else if ( rExecPl.loop && ! rExecPl.canceled ) + { + // Loop back to first stage + rExecPl.stage = StageId(0); } else { - // Next stage is 1 past the last stage. Finished running - rExecPl.stage = lgrn::id_null(); - rExecPl.running = false; + // Finished running + rExecPl.stage = lgrn::id_null(); + rExecPl.running = false; + rExecPl.canceled = false; + rExecPl.loop = false; } - - // asserted by pipeline_can_advance: - // * rExecPl.ownStageReqTasksLeft == 0; - // * rExecPl.tasksReqOwnStageLeft == 0; } static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) @@ -149,11 +158,15 @@ static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, Ex rExec.tasksQueuedBlocked.erase(task); } } - else if (auto const [reqPipeline, reqStage] = tasks.m_taskRunOn[task]; - stage_is_cancelled(tasks, rExec, rExec.plData[reqPipeline], reqPipeline, reqStage)) + else { - // Task is cancelled - -- rExecPl.tasksReqOwnStageLeft; + auto const [reqPipeline, reqStage] = tasks.m_taskRunOn[task]; + ExecPipeline &rReqExecPl = rExec.plData[reqPipeline]; + if ( ! rReqExecPl.running || rReqExecPl.canceled) + { + // Task is done or cancelled + -- rExecPl.tasksReqOwnStageLeft; + } } } @@ -175,7 +188,7 @@ static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, Ex { return true; // Not running, which means the whole pipeline finished already } - else if (stage_is_cancelled(tasks, rExec, rReqTaskExecPl, stgreqtask.reqPipeline, stgreqtask.reqStage)) + else if (rReqTaskExecPl.canceled) { return true; // Stage cancelled. Required task is considered finish and will never run } @@ -187,7 +200,7 @@ static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, Ex { return true; // Passed required stage. Required task finished } - else if ( ! rReqTaskExecPl.tasksQueued ) + else if ( ! rReqTaskExecPl.tasksQueueDone ) { return false; // Required tasks not queued yet } @@ -218,10 +231,9 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe return; } - bool const stageCancelled = rExecPl.cancelOptionals && tasks.m_pipelineControl[pipeline].optionalStages.test(std::size_t(rExecPl.stage)); bool noTasksRun = true; - if ( ! stageCancelled ) + if ( ! rExecPl.canceled ) { auto const anystg = anystg_from(graph, pipeline, rExecPl.stage); auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; @@ -262,7 +274,7 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe } } - rExecPl.tasksQueued = true; + rExecPl.tasksQueueDone = true; if (noTasksRun && pipeline_can_advance(graph, rExec, rExecPl)) { @@ -275,88 +287,70 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe } -static void run_pipeline_recurse(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +static void run_pipeline_recurse(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline, bool loop = false) { - ExecPipeline &rExecPl = rExec.plData[pipeline]; - if (fanout_size(graph.pipelineToFirstAnystg, pipeline) != 0) - { - rExecPl.running = true; - rExecPl.doLoop = tasks.m_pipelineControl[pipeline].loops; - rExecPl.cancelOptionals = false; - if (rExecPl.ownStageReqTasksLeft == 0) + //auto const children = ArrayView{fanout_view(graph.pipelineToFirstChild, graph.childPlToParent, pipeline)}; + + //for (PipelineId const plSub : children) + //{ + // run_pipeline_recurse(tasks, graph, rExec, PipelineId(plSub), loop); + //} +} + +static void cancel_stage(TaskGraph const& graph, ExecContext& rExec, AnyStageId const anystg) +{ + auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; + + for (TaskId task : runTasks) + { + // Stages depend on this RunTask (reverse Stage-requires-Task) + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) { - rExec.plAdvance.set(std::size_t(pipeline)); - rExec.hasPlAdvance = true; + PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); + ExecPipeline &rReqExecPl = rExec.plData[reqPl]; + + if (rReqExecPl.stage == reqStg) + { + LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); + -- rReqExecPl.ownStageReqTasksLeft; + pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); + } } - } - auto const children = ArrayView{fanout_view(graph.pipelineToFirstChild, graph.childPlToParent, pipeline)}; + // RunTask depends on stages (Task-requires-Stage) + for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) + { + ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; - for (PipelineId const plSub : children) - { - run_pipeline_recurse(tasks, graph, rExec, PipelineId(plSub)); + if (rReqExecPl.stage == req.reqStage) + { + LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); + -- rReqExecPl.tasksReqOwnStageLeft; + pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); + } + } } } -void pipeline_cancel_optionals(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) +void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) { - if (rExecPl.cancelOptionals) + if (rExecPl.canceled) { return; // already cancelled } - StageBits_t const optionalStages = tasks.m_pipelineControl[pipeline].optionalStages; int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); auto anystgInt = uint32_t(anystg_from(graph, pipeline, rExecPl.stage)); - - // For each cancelled stage for (auto stgInt = int(rExecPl.stage); stgInt < stageCount; ++stgInt, ++anystgInt) { - if ( ! optionalStages.test(stgInt) ) - { - continue; - } - - auto const anystg = AnyStageId(anystgInt); - - auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; - - for (TaskId task : runTasks) - { - // Stages depend on this RunTask (reverse Stage-requires-Task) - for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) - { - PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; - StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); - ExecPipeline &rReqExecPl = rExec.plData[reqPl]; - - if (rReqExecPl.stage == reqStg) - { - LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); - -- rReqExecPl.ownStageReqTasksLeft; - pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); - } - } - - // RunTask depends on stages (Task-requires-Stage) - for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) - { - ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; - - if (rReqExecPl.stage == req.reqStage) - { - LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); - -- rReqExecPl.tasksReqOwnStageLeft; - pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); - } - } - } + cancel_stage(graph, rExec, AnyStageId(anystgInt)); } - rExecPl.cancelOptionals = true; + rExecPl.canceled = true; } void pipeline_cancel_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) @@ -368,6 +362,22 @@ void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe { exec_log(rExec, ExecContext::EnqueueStart{}); + auto run_pipeline = [&graph, &rExec] (PipelineId const pipeline, bool const loop) + { + ExecPipeline &rExecPl = rExec.plData[pipeline]; + if (fanout_size(graph.pipelineToFirstAnystg, pipeline) != 0) + { + rExecPl.running = true; + rExecPl.loop = loop; + + if (rExecPl.ownStageReqTasksLeft == 0) + { + rExec.plAdvance.set(std::size_t(pipeline)); + rExec.hasPlAdvance = true; + } + } + }; + if (rExec.hasRequestRun) { for (ExecPipeline const& rExecPl : rExec.plData) @@ -378,7 +388,37 @@ void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe for (PipelineInt const plInt : rExec.plRequestRun.ones()) { auto const pipeline = PipelineId(plInt); - run_pipeline_recurse(tasks, graph, rExec, pipeline); + + PipelineTreePos_t const treePos = graph.pipelineToPltree[pipeline]; + + if (treePos == lgrn::id_null()) + { + // Pipeline not in tree + run_pipeline(pipeline, tasks.m_pipelineControl[pipeline].loops); + } + else + { + uint32_t const descendants = graph.pltreeDescendantCounts[treePos]; + PipelineTreePos_t const lastPos = treePos + 1 + descendants; + + uint32_t loopNextN = 0; + + // Iterate descendents + for (PipelineTreePos_t run = treePos; run < lastPos; ++run) + { + PipelineId const runPipeline = graph.pltreeToPipeline[run]; + + if (tasks.m_pipelineControl[runPipeline].loops) + { + // Loop this pipeline and all of its descendants + loopNextN = std::max(loopNextN, 1 + graph.pltreeDescendantCounts[run]); + } + + run_pipeline(runPipeline, loopNextN != 0); + + loopNextN -= (loopNextN != 0); + } + } } rExec.plRequestRun.reset(); rExec.hasRequestRun = false; @@ -393,7 +433,7 @@ void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe for (PipelineInt const plInt : rExec.plAdvance.ones()) { - pipeline_advance_stage(graph, rExec, PipelineId(plInt)); + pipeline_advance_stage(tasks, graph, rExec, PipelineId(plInt)); } for (PipelineInt const plInt : rExec.plAdvance.ones()) @@ -466,276 +506,11 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe // Handle actions - if (actions & TaskAction::CancelOptionalStages) + if (actions & TaskAction::Cancel) { - pipeline_cancel_optionals(tasks, graph, rExec, rExecPl, pipeline); + pipeline_cancel(tasks, graph, rExec, rExecPl, pipeline); } } } // namespace osp - - - -#if 0 - - - // Step 2. Run pipelines requested to run - for (PipelineInt const plInt : rExec.plRunning.ones()) - { - auto const pipeline = PipelineId(plInt); - - StageInt const stgCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); - - // Run all tasks? - for (StageInt stgInt = 0; stgInt < stgCount; ++stgInt) - { - auto const stg = StageId(stgInt); - auto const anystg = anystg_from(graph, pipeline, stg); - auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; - - for (TaskId const task : runTasks) - { - // there's nothing to do here? - } - } - } - rExec.plRunning.reset(); - - - -static void run_pipeline_tasks(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) -{ - ExecPipeline &rExecPl = rExec.plData[pipeline]; - - if ( ! rExecPl.stageChanged ) - { - return; - } - - if (rExecPl.nextStage == lgrn::id_null()) - { - return; - } - - LGRN_ASSERT(rExecPl.tasksQueuedBlocked == 0); - LGRN_ASSERT(rExecPl.tasksQueuedRun == 0); - - if ( ! rExecPl.triggerUsed ) - { - return; // Not triggered - } - - // Enqueue all tasks - - AnyStageId const anystg = anystg_from(graph, pipeline, rExecPl.nextStage); - - auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; - - if (runTasks.size() == 0) - { - // No tasks to run. RunTasks are responsible for setting this pipeline dirty once they're - // all done. If there is none, then this pipeline may get stuck if nothing sets it dirty, - // so set dirty right away. - rExec.plDirtyNext.set(std::size_t(pipeline)); - return; - } - - for (TaskId task : runTasks) - { - bool const runsOnManyPipelines = fanout_size(graph.taskToFirstRunstage, task) > 1; - bool const alreadyBlocked = rExec.tasksQueuedBlocked.contains(task); - bool const alreadyRunning = rExec.tasksQueuedRun .contains(task); - bool const alreadyQueued = alreadyBlocked || alreadyRunning; - - LGRN_ASSERTM( ( ! alreadyQueued ) || runsOnManyPipelines, - "Attempt to enqueue a single-stage task that is already running. This is impossible!"); - - if (alreadyBlocked) - { - ++ rExecPl.tasksQueuedBlocked; - continue; - } - - - } -} - - - - - // Evaluate Stage-requires-Tasks - // * Calculate stageReqTaskCount as the number of required task that are currently queued - AnyStageId const anystg = anystg_from(graph, pipeline, newStage); - int stageReqTaskCount = 0; - - for (StageRequiresTask const& stgreqtask : fanout_view(graph.anystgToFirstStgreqtask, graph.stgreqtaskData, anystg)) - { - if (rExec.tasksQueuedBlocked.contains(stgreqtask.reqTask) - || rExec.tasksQueuedRun .contains(stgreqtask.reqTask)) - { - ++ stageReqTaskCount; - } - } - - rExecPl.stageReqTaskCount = stageReqTaskCount; - - // Evaluate Task-requires-Stages - // * Increment counts for queued tasks that depend on this stage. This unblocks tasks - - for (TaskId const& task : fanout_view(graph.anystgToFirstRevTaskreqstg, graph.revTaskreqstgToTask, anystg)) - { - if (rExec.tasksQueuedBlocked.contains(task)) - { - BlockedTask &rBlocked = rExec.tasksQueuedBlocked.get(task); - -- rBlocked.remainingTaskReqStg; - if (rBlocked.remainingTaskReqStg == 0) - { - exec_log(rExec, ExecContext::UnblockTask{task}); - ExecPipeline &rTaskPlExec = rExec.plData[rBlocked.pipeline]; - -- rTaskPlExec.tasksQueuedBlocked; - ++ rTaskPlExec.tasksQueuedRun; - rExec.tasksQueuedRun.emplace(task); - rExec.tasksQueuedBlocked.erase(task); - } - } - } - - - - - - // Evaluate Stage-requires-Tasks - // * Increment counts for currently running stages that require this task - for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) - { - PipelineId const reqTaskPl = graph.anystgToPipeline[reqTaskAnystg]; - StageId const reqTaskStg = stage_from(graph, reqTaskPl, reqTaskAnystg); - ExecPipeline &rReqTaskPlData = rExec.plData[reqTaskPl]; - - if (rReqTaskPlData.currentStage == reqTaskStg) - { - ++ rReqTaskPlData.stageReqTaskCount; - } - } - - // Evaluate Task-requires-Stages - // * Increment counts for each required stage - // * Determine which requirements are already satisfied - auto const taskreqstageView = ArrayView(fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)); - int reqsRemaining = taskreqstageView.size(); - for (TaskRequiresStage const& req : taskreqstageView) - { - AnyStageId const reqAnystg = anystg_from(graph, req.reqPipeline, req.reqStage); - ExecPipeline &rReqPlData = rExec.plData[req.reqPipeline]; - - ++ rExec.anystgReqByTaskCount[reqAnystg]; - ++ rReqPlData.taskReqStageCount; - - if (rReqPlData.currentStage == req.reqStage) - { - reqsRemaining --; - } - else if ( rReqPlData.tasksQueuedRun == 0 - && rReqPlData.tasksQueuedBlocked == 0 ) - { - rExec.plDirtyNext.set(std::size_t(req.reqPipeline)); - } - } - - bool const blocked = reqsRemaining != 0; - - if (blocked) - { - rExec.tasksQueuedBlocked.emplace(task, BlockedTask{reqsRemaining, pipeline}); - ++ rExecPl.tasksQueuedBlocked; - } - else - { - // Task can run right away - rExec.tasksQueuedRun.emplace(task); - ++ rExecPl.tasksQueuedRun; - } - - exec_log(rExec, ExecContext::EnqueueTask{pipeline, rExecPl.nextStage, task, blocked}); - if (blocked) - { - for (TaskRequiresStage const& req : taskreqstageView) - { - ExecPipeline const &reqPlData = rExec.plData[req.reqPipeline]; - - if (reqPlData.currentStage != req.reqStage) - { - exec_log(rExec, ExecContext::EnqueueTaskReq{req.reqPipeline, req.reqStage}); - } - } - } - - - - - auto const try_set_dirty = [&rExec] (ExecPipeline &plExec, PipelineId pipeline) - { - if ( plExec.tasksQueuedRun == 0 - && plExec.tasksQueuedBlocked == 0 - && plExec.stageReqTaskCount == 0) - { - rExec.plDirty.set(std::size_t(pipeline)); - } - }; - - // Handle task running on stage - for (AnyStageId const anystg : fanout_view(graph.taskToFirstRunstage, graph.runstageToAnystg, task)) - { - PipelineId const pipeline = graph.anystgToPipeline[anystg]; - ExecPipeline &plExec = rExec.plData[pipeline]; - - -- plExec.tasksQueuedRun; - try_set_dirty(plExec, pipeline); - } - - // Handle stages requiring this task - for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) - { - PipelineId const reqTaskPl = graph.anystgToPipeline[reqTaskAnystg]; - StageId const reqTaskStg = stage_from(graph, reqTaskPl, reqTaskAnystg); - ExecPipeline &rReqTaskPlData = rExec.plData[reqTaskPl]; - - if (rReqTaskPlData.currentStage == reqTaskStg) - { - -- rReqTaskPlData.stageReqTaskCount; - - try_set_dirty(rReqTaskPlData, reqTaskPl); - } - } - - // Handle this task requiring stages - for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) - { - AnyStageId const reqAnystg = anystg_from(graph, req.reqPipeline, req.reqStage); - -- rExec.anystgReqByTaskCount[reqAnystg]; - -- rExec.plData[req.reqPipeline].taskReqStageCount; - } - - // Trigger specified stages based on return value - auto const triggersView = ArrayView(fanout_view(graph.taskToFirstTrigger, graph.triggerToPlStage, task)); - for (int i = 0; i < triggersView.size(); ++i) - { - if (dirty.test(i)) - { - auto const [pipeline, stage] = triggersView[i]; - ExecPipeline &plExec = rExec.plData[pipeline]; - - if ( ! plExec.triggered.test(std::size_t(stage)) ) - { - plExec.triggered.set(std::size_t(stage)); - exec_log(rExec, ExecContext::CompleteTaskTrigger{pipeline, stage}); - } - - try_set_dirty(plExec, pipeline); - } - } - -#endif - - diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index fd82810c..74b723de 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -47,10 +47,11 @@ struct ExecPipeline int ownStageReqTasksLeft{0}; StageId stage { lgrn::id_null() }; - bool tasksQueued { false }; + + bool tasksQueueDone { false }; + bool loop { false }; bool running { false }; - bool doLoop { false }; - bool cancelOptionals { false }; + bool canceled { false }; }; struct BlockedTask diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index a1eef38d..f1a52379 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -24,6 +24,8 @@ */ #include "tasks.h" +#include "../bitvector.h" + #include namespace osp @@ -46,8 +48,10 @@ struct PipelineCounts { std::array stageCounts; - uint16_t children {0}; uint8_t stages {0}; + + PipelineId firstChild { lgrn::id_null() }; + PipelineId sibling { lgrn::id_null() }; }; @@ -60,14 +64,16 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView KeyedVec plCounts; KeyedVec taskCounts; + BitVector_t plInTree; + out.pipelineToFirstAnystg .resize(maxPipelines); + bitvector_resize(plInTree, maxPipelines); plCounts .resize(maxPipelines+1); taskCounts .resize(maxTasks+1); std::size_t totalTasksReqStage = 0; std::size_t totalStageReqTasks = 0; std::size_t totalRunTasks = 0; - std::size_t totalChildren = 0; std::size_t totalStages = 0; // 1. Count total number of stages @@ -127,19 +133,32 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView totalStageReqTasks += pEdges->m_syncWith.size(); } - // 3. Count pipeline children + // 3. Map out children and siblings in tree - for (PipelineInt const pipelineInt : tasks.m_pipelineIds.bitview().zeros()) + for (PipelineInt const childPlInt : tasks.m_pipelineIds.bitview().zeros()) { - PipelineId const parent = tasks.m_pipelineParents[PipelineId(pipelineInt)]; + PipelineId const child = PipelineId(childPlInt); + PipelineId const parent = tasks.m_pipelineParents[child]; if (parent != lgrn::id_null()) { - ++ plCounts[parent].children; - ++ totalChildren; + plInTree.set(std::size_t(parent)); + plInTree.set(std::size_t(child)); + + PipelineCounts &rChildCounts = plCounts[child]; + PipelineCounts &rParentCounts = plCounts[parent]; + + if (rParentCounts.firstChild != lgrn::id_null()) + { + rChildCounts.sibling = rParentCounts.firstChild; + } + + rParentCounts.firstChild = child; } } + std::size_t const treeSize = plInTree.count(); + // 4. Allocate // The +1 is needed for 1-to-many connections to store the total number of other elements they @@ -149,8 +168,6 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView out.anystgToPipeline .resize(totalStages+1, lgrn::id_null()); out.anystgToFirstRuntask .resize(totalStages+1, lgrn::id_null()); out.runtaskToTask .resize(totalRunTasks, lgrn::id_null()); - out.pipelineToFirstChild .resize(maxPipelines+1, lgrn::id_null()); - out.childPlToParent .resize(totalChildren, lgrn::id_null()); out.anystgToFirstStgreqtask .resize(totalStages+1, lgrn::id_null()); out.stgreqtaskData .resize(totalStageReqTasks, {}); out.taskToFirstRevStgreqtask .resize(maxTasks+1, lgrn::id_null()); @@ -159,6 +176,9 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView out.taskreqstgData .resize(totalTasksReqStage, {}); out.anystgToFirstRevTaskreqstg .resize(totalStages+1, lgrn::id_null()); out.revTaskreqstgToTask .resize(totalTasksReqStage, lgrn::id_null()); + out.pltreeDescendantCounts .resize(treeSize, 0); + out.pltreeToPipeline .resize(treeSize, lgrn::id_null()); + out.pipelineToPltree .resize(maxPipelines, lgrn::id_null()); // 5. Calculate one-to-many partitions @@ -181,11 +201,6 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView }, [] (AnyStageId, RunTaskId) { }); - fanout_partition( - out.pipelineToFirstChild, - [&plCounts] (PipelineId pl) { return plCounts[pl].children; }, - [&out] (PipelineId, ChildPipelineId) { }); - fanout_partition( out.anystgToFirstStgreqtask, [&plCounts, &out] (AnyStageId stg) @@ -237,10 +252,8 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView -- rStageCounts.runTasks; } - for (TaskEdges const* pEdges : data) { - for (auto const [task, pipeline, stage] : pEdges->m_syncWith) { AnyStageId const anystg = anystg_from(out, pipeline, stage); @@ -282,13 +295,6 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView } } - for (PipelineInt const pipelineInt : tasks.m_pipelineIds.bitview().zeros()) - { - // PipelineId const parent = tasks.m_pipelineParents[PipelineId(pipelineInt)]; - - // TODO - } - [[maybe_unused]] auto const all_counts_zero = [&] () { if ( totalStageReqTasks != 0 @@ -321,6 +327,56 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView LGRN_ASSERTM(all_counts_zero(), "Counts repurposed as items remaining, and must all be zero by the end here"); + + // 7. Build Pipeline Tree + + auto const add_subtree = [&] (auto const& self, PipelineId const root, PipelineId const firstChild, PipelineTreePos_t const pos) -> uint32_t + { + out.pltreeToPipeline[pos] = root; + out.pipelineToPltree[root] = pos; + + uint32_t descendantCount = 0; + + PipelineId child = firstChild; + + PipelineTreePos_t childPos = pos; + + while (child != lgrn::id_null()) + { + PipelineCounts const& rChildCounts = plCounts[child]; + + ++ childPos; + uint32_t const childDescendantCount = self(self, child, rChildCounts.firstChild, childPos); + descendantCount += 1 + childDescendantCount; + + child = rChildCounts.sibling; + } + + out.pltreeDescendantCounts[pos] = descendantCount; + + return descendantCount; + }; + + PipelineTreePos_t rootPos = 0; + + for (PipelineInt const pipelineInt : tasks.m_pipelineIds.bitview().zeros()) + { + auto const pipeline = PipelineId(pipelineInt); + if ( ! plInTree.test(pipelineInt) || tasks.m_pipelineParents[pipeline] != lgrn::id_null()) + { + continue; // Not in tree or not a root + } + + // For each root pipeline + + PipelineCounts const& rRootCounts = plCounts[pipeline]; + + uint32_t const rootDescendantCount = add_subtree(add_subtree, pipeline, rRootCounts.firstChild, rootPos); + + rootPos += 1 + rootDescendantCount; + } + + return out; } diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 709f8c62..759fd64b 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -78,7 +78,6 @@ struct PipelineInfo struct PipelineControl { - StageBits_t optionalStages; bool loops; }; @@ -125,13 +124,13 @@ struct TaskEdges std::vector m_semaphoreEdges; }; +using PipelineTreePos_t = uint32_t; + enum class AnyStageId : uint32_t { }; enum class RunTaskId : uint32_t { }; enum class RunStageId : uint32_t { }; -enum class ChildPipelineId : uint32_t { }; - enum class StageReqTaskId : uint32_t { }; enum class ReverseStageReqTaskId : uint32_t { }; @@ -171,11 +170,6 @@ struct TaskGraph KeyedVec anystgToFirstRuntask; KeyedVec runtaskToTask; - // Each pipeline has multiple child pipelines - // PipelineId --> PipelineChildId --> many PipelineId - KeyedVec pipelineToFirstChild; - KeyedVec childPlToParent; - // Each stage has multiple entrance requirements. // AnyStageId <--> many StageEnterReqId KeyedVec anystgToFirstStgreqtask; @@ -194,6 +188,13 @@ struct TaskGraph KeyedVec anystgToFirstRevTaskreqstg; KeyedVec revTaskreqstgToTask; + // N-ary tree structure represented as an array of descendant counts. Each node's subtree of + // descendants is positioned directly after it within the array. + // Example for tree structure "A( B(C(D)), E(F(G(H,I))) )" + // * Descendant Count array: [A:8, B:2, C:1, D:0, E:4, F:3, G:2, H:0, I:0] + KeyedVec pltreeDescendantCounts; + KeyedVec pltreeToPipeline; + KeyedVec pipelineToPltree; // not yet used lgrn::IntArrayMultiMap taskAcquire; /// Tasks acquire (n) Semaphores @@ -314,10 +315,6 @@ struct PipelineDef PipelineInfo::stage_type_t m_type { PipelineInfo::stage_type_family_t::value }; PipelineId m_value { lgrn::id_null() }; - - - - }; using PipelineDefBlank_t = PipelineDef; diff --git a/src/osp/tasks/worker.h b/src/osp/tasks/worker.h index 074709af..66b5c94b 100644 --- a/src/osp/tasks/worker.h +++ b/src/osp/tasks/worker.h @@ -32,8 +32,8 @@ namespace osp enum class TaskAction { - CancelLoop = 1 << 0, - CancelOptionalStages = 1 << 1 + Cancel = 1 << 0 + // CancelLoop = 1 << 1 }; using TaskActions = Corrade::Containers::EnumSet; diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 4b2cc37d..4a0f6156 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -89,7 +89,6 @@ struct Pipelines // Test pipeline consisting of parallel tasks TEST(Tasks, BasicSingleThreadedParallelTasks) { - return; using namespace test_a; using enum Stages; @@ -103,9 +102,9 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; - constexpr int sc_repetitions = 32; + constexpr int sc_repetitions = 32; constexpr int sc_pusherTaskCount = 24; - constexpr int sc_totalTaskCount = sc_pusherTaskCount + 2; + constexpr int sc_totalTaskCount = sc_pusherTaskCount + 2; std::mt19937 randGen(69); // Step 1: Create tasks @@ -158,7 +157,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) exec_resize(tasks, graph, exec); int checksRun = 0; - int input = 0; + int input = 0; std::vector output; // Repeat with randomness to test many possible execution orders @@ -202,7 +201,7 @@ struct Pipelines osp::PipelineDef distraction; }; -} // namespace test_gameworld +} // namespace test_b // Test that features a looping 'normal' pipeline and an 'optional' pipeline that has a 50% chance of running TEST(Tasks, BasicSingleThreadedOptional) @@ -210,7 +209,7 @@ TEST(Tasks, BasicSingleThreadedOptional) using namespace test_b; using enum Stages; - using BasicTraits_t = BasicBuilderTraits; + using BasicTraits_t = BasicBuilderTraits; using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; @@ -224,9 +223,11 @@ TEST(Tasks, BasicSingleThreadedOptional) auto const pl = builder.create_pipelines(); - tasks.m_pipelineControl[pl.optional].optionalStages.set(std::size_t(Write)); + builder.pipeline(pl.optional) + .parent(pl.normal); - // These tasks run in a loop, triggering each other to run continuously + builder.pipeline(pl.distraction) + .parent(pl.normal); builder.task() .run_on ({pl.optional(Schedule)}) @@ -235,11 +236,11 @@ TEST(Tasks, BasicSingleThreadedOptional) if (rRand() % 2 == 0) { rState.expectOptionalDone = true; - return {}; + return { }; } else { - return TaskAction::CancelOptionalStages; + return TaskAction::Cancel; } }); @@ -308,8 +309,6 @@ TEST(Tasks, BasicSingleThreadedOptional) for (int i = 0; i < sc_repetitions; ++i) { pipeline_run(exec, pl.normal); - pipeline_run(exec, pl.optional); - pipeline_run(exec, pl.distraction); enqueue_dirty(tasks, graph, exec); randomized_singlethreaded_execute( @@ -323,7 +322,94 @@ TEST(Tasks, BasicSingleThreadedOptional) // Assure that the tasks above actually ran, and didn't just skip everything // Max of 5 tasks run each loop ASSERT_GT(world.checks, sc_repetitions / 5); +} + +//----------------------------------------------------------------------------- + +namespace test_c +{ + +struct TestState +{ + std::vector in; + std::vector out; + + int processA{0}; + int processB{0}; +}; + +enum class Stages { Schedule, Process, Done, Clear }; + +struct Pipelines +{ + osp::PipelineDef main; + osp::PipelineDef loop; + osp::PipelineDef processA; + osp::PipelineDef processB; +}; + +} // namespace test_c + +// +TEST(Tasks, BasicSingleThreadedLoop) +{ + using namespace test_c; + using enum Stages; + + using BasicTraits_t = BasicBuilderTraits; + using Builder_t = BasicTraits_t::Builder; + using TaskFuncVec_t = BasicTraits_t::FuncVec_t; + + constexpr int sc_repetitions = 128; + std::mt19937 randGen(69); + + Tasks tasks; + TaskEdges edges; + TaskFuncVec_t functions; + Builder_t builder{tasks, edges, functions}; + auto const pl = builder.create_pipelines(); + + builder.pipeline(pl.loop) .parent(pl.main).loops(true); + builder.pipeline(pl.processA).parent(pl.loop); + builder.pipeline(pl.processB).parent(pl.loop); + + builder.task() + .run_on ({pl.main(Schedule)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions + { + return { }; + }); + + builder.task() + .run_on ({pl.loop(Schedule)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions + { + return { }; + }); + + + TaskGraph const graph = make_exec_graph(tasks, {&edges}); + + // Execute + + ExecContext exec; + exec_resize(tasks, graph, exec); + + TestState world; + + for (int i = 0; i < sc_repetitions; ++i) + { + pipeline_run(exec, pl.main); + enqueue_dirty(tasks, graph, exec); + + randomized_singlethreaded_execute( + tasks, graph, exec, randGen, 999999, + [&functions, &world, &randGen] (TaskId const task) -> TaskActions + { + return functions[task](world, randGen); + }); + } } //----------------------------------------------------------------------------- From 181a038d76279fbfe3000ca4f12067417c608267 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 31 Jul 2023 00:52:59 -0700 Subject: [PATCH 26/35] Implement Pipeline Loops --- src/osp/tasks/builder.h | 8 +- src/osp/tasks/execute.cpp | 583 ++++++++++++++++++++++++-------------- src/osp/tasks/execute.h | 23 +- src/osp/tasks/tasks.cpp | 20 +- src/osp/tasks/tasks.h | 3 +- 5 files changed, 401 insertions(+), 236 deletions(-) diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index e0ba0ee0..21d8520c 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -97,12 +97,16 @@ struct TaskBuilderBase m_rTasks.m_pipelineParents.resize(capacity, lgrn::id_null()); TGT_STRUCT_T out; - auto const members = Corrade::Containers::staticArrayView(reinterpret_cast(&out)); + + // Set m_value members of TGT_STRUCT_T, asserted to contain only PipelineDef<...> + // This is janky enough that rewriting the code below might cause it to ONLY SEGFAULT ON + // RELEASE AND ISN'T CAUGHT BY ASAN WTF??? (on gcc 11) + auto *pOutBytes = reinterpret_cast(std::addressof(out)); for (std::size_t i = 0; i < count; ++i) { PipelineId const pl = pipelines[i]; - members[i].m_value = pl; + *reinterpret_cast(pOutBytes + sizeof(PipelineDefBlank_t)*i + offsetof(PipelineDefBlank_t, m_value)) = pl; } return out; diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 13be36d5..84e4e8c7 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -30,62 +30,213 @@ namespace osp { -void exec_resize(Tasks const& tasks, ExecContext &rOut) -{ - std::size_t const maxTasks = tasks.m_taskIds.capacity(); - std::size_t const maxPipeline = tasks.m_pipelineIds.capacity(); +static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) noexcept; - rOut.tasksQueuedRun .reserve(maxTasks); - rOut.tasksQueuedBlocked.reserve(maxTasks); - rOut.plData.resize(maxPipeline); - bitvector_resize(rOut.plAdvance, maxPipeline); - bitvector_resize(rOut.plAdvanceNext, maxPipeline); - bitvector_resize(rOut.plRequestRun, maxPipeline); -} +static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept; -void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) -{ - exec_resize(tasks, rOut); -} +static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept; + +static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept; + +static constexpr bool pipeline_can_advance(TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept; + +static inline void pipeline_try_advance(TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) noexcept; -void pipeline_run(ExecContext &rExec, PipelineId const pipeline) +static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) noexcept; + +struct ArgsForIsPipelineInLoop { - rExec.plRequestRun.set(std::size_t(pipeline)); - rExec.hasRequestRun = true; -} + PipelineId viewedFrom; + PipelineId insideLoop; +}; + +static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, ArgsForIsPipelineInLoop const args) noexcept; + +//----------------------------------------------------------------------------- +// Main entry-point functions -static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) +void exec_update(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept { - if (rExec.doLogging) + auto const run_pipeline = [&graph, &rExec] (ExecPipeline& rExecPl, PipelineId const pipeline, bool const loop) { - rExec.logMsg.push_back(msg); + if (fanout_size(graph.pipelineToFirstAnystg, pipeline) != 0) + { + rExecPl.running = true; + rExecPl.loop = loop; + + LGRN_ASSERT(rExecPl.ownStageReqTasksLeft == 0); + { + rExec.plAdvance.set(std::size_t(pipeline)); + rExec.hasPlAdvanceOrLoop = true; + } + } + }; + + exec_log(rExec, ExecContext::EnqueueStart{}); + + if (rExec.hasRequestRun) + { + for (ExecPipeline const& rExecPl : rExec.plData) + { + LGRN_ASSERTM( ! rExecPl.running, "Running new pipelines while already running is not yet supported ROFL!"); + } + + for (PipelineInt const plInt : rExec.plRequestRun.ones()) + { + auto const pipeline = PipelineId(plInt); + + PipelineTreePos_t const treePos = graph.pipelineToPltree[pipeline]; + + if (treePos == lgrn::id_null()) + { + // Pipeline not in tree + run_pipeline(rExec.plData[pipeline], pipeline, tasks.m_pipelineControl[pipeline].loops); + } + else + { + uint32_t const descendants = graph.pltreeDescendantCounts[treePos]; + PipelineTreePos_t const lastPos = treePos + 1 + descendants; + + uint32_t loopNextN = 0; + + for (PipelineTreePos_t runPos = treePos; runPos < lastPos; ++runPos) + { + PipelineId const runPipeline = graph.pltreeToPipeline[runPos]; + ExecPipeline &rRunExecPl = rExec.plData[runPipeline]; + + if (tasks.m_pipelineControl[runPipeline].loops) + { + uint32_t const runDescendants = graph.pltreeDescendantCounts[runPos]; + rRunExecPl.loopPipelinesLeft = 1 + runDescendants; + + // Loop this pipeline and all of its descendants + loopNextN = std::max(loopNextN, 1 + runDescendants); + } + + run_pipeline(rRunExecPl, runPipeline, loopNextN != 0); + + loopNextN -= (loopNextN != 0); + } + } + } + rExec.plRequestRun.reset(); + rExec.hasRequestRun = false; } -} -static bool pipeline_is_ancestor_descendant(Tasks const& tasks, TaskGraph const& graph, PipelineId const ancestor, PipelineId const descendant) -{ - //tasks. -} -static constexpr bool pipeline_can_advance(TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept -{ - return rExecPl.ownStageReqTasksLeft == 0 // Tasks required by stage are done - && rExecPl.tasksReqOwnStageLeft == 0 // Not required by any tasks - && (rExecPl.tasksQueuedBlocked+rExecPl.tasksQueuedRun) == 0; // Tasks done + while (rExec.hasPlAdvanceOrLoop) + { + exec_log(rExec, ExecContext::EnqueueCycle{}); + + rExec.hasPlAdvanceOrLoop = false; + + for (auto const [pipeline, treePos] : rExec.requestLoop) + { + ExecPipeline &rExecPl = rExec.plData[pipeline]; + LGRN_ASSERT(rExecPl.loopPipelinesLeft == 0); + + + } + rExec.requestLoop.clear(); + + for (PipelineInt const plInt : rExec.plAdvance.ones()) + { + pipeline_advance_stage(tasks, graph, rExec, PipelineId(plInt)); + } + for (PipelineInt const plInt : rExec.plAdvance.ones()) + { + pipeline_advance_reqs(tasks, graph, rExec, PipelineId(plInt)); + } + + for (PipelineInt const plInt : rExec.plAdvance.ones()) + { + pipeline_advance_run(tasks, graph, rExec, PipelineId(plInt)); + } + + std::copy(rExec.plAdvanceNext.ints().begin(), + rExec.plAdvanceNext.ints().end(), + rExec.plAdvance .ints().begin()); + rExec.plAdvanceNext.reset(); + } + + exec_log(rExec, ExecContext::EnqueueEnd{}); } -static inline void pipeline_try_advance(TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) +void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TaskActions actions) noexcept { - if (pipeline_can_advance(graph, rExec, rExecPl)) + LGRN_ASSERT(rExec.tasksQueuedRun.contains(task)); + rExec.tasksQueuedRun.erase(task); + + exec_log(rExec, ExecContext::CompleteTask{task}); + + auto const [pipeline, stage] = tasks.m_taskRunOn[task]; + ExecPipeline &rExecPl = rExec.plData[pipeline]; + + -- rExecPl.tasksQueuedRun; + + pipeline_try_advance(graph, rExec, rExecPl, pipeline); + + if (actions & TaskAction::Cancel) { - rExec.plAdvance.set(std::size_t(pipeline)); - rExec.hasPlAdvance = true; + LGRN_ASSERTMV(rExecPl.tasksQueuedRun == 0 && rExecPl.tasksQueuedBlocked == 0, + "Tasks that cancel the pipeline should be the only task running. Too lazy to enforce this elsewhere LOL", + rExecPl.tasksQueuedRun, rExecPl.tasksQueuedBlocked); + pipeline_cancel(tasks, graph, rExec, rExecPl, pipeline); } -}; -static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) + // Handle stages requiring this task + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) + { + PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); + ExecPipeline &rReqExecPl = rExec.plData[reqPl]; + + if (rReqExecPl.stage == reqStg) + { + if ( ! (rExecPl.loop && is_pipeline_in_loop(tasks, graph, {.viewedFrom = reqPl, .insideLoop = pipeline})) || rExecPl.canceled ) + { + -- rReqExecPl.ownStageReqTasksLeft; + + pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); + } + // else: This pipeline is enclosed in a loop relative to dependency. This task may run + // more times. + } + else + { + LGRN_ASSERTMV(int(rReqExecPl.stage) < int(reqStg) && rReqExecPl.stage != lgrn::id_null(), + "Stage-requires-Task means that rReqExecPl.stage cannot advance any further than reqStg until task completes.", + int(task), int(rReqExecPl.stage), int(reqStg)); + } + } + + // Handle this task requiring stages from other pipelines + for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) + { + ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; + + LGRN_ASSERTMV(rReqExecPl.stage == req.reqStage, + "Task-requires-Stage means this task should have not run unless the stage is selected", + int(task), int(rReqExecPl.stage), int(req.reqStage)); + + if ( ! (rExecPl.loop && is_pipeline_in_loop(tasks, graph, {.viewedFrom = req.reqPipeline, .insideLoop = pipeline})) || rExecPl.canceled ) + { + -- rReqExecPl.tasksReqOwnStageLeft; + + pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); + } + // else: This pipeline is enclosed in a loop relative to dependency. This task may run + // more times. + } +} + +//----------------------------------------------------------------------------- + +// Major steps + +static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept { ExecPipeline &rExecPl = rExec.plData[pipeline]; @@ -107,10 +258,57 @@ static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, E // Proceed to next stage since its valid rExecPl.stage = nextStage; } - else if ( rExecPl.loop && ! rExecPl.canceled ) + else if ( rExecPl.loop ) { - // Loop back to first stage - rExecPl.stage = StageId(0); + // Loop done + rExecPl.stage = lgrn::id_null(); + + PipelineTreePos_t const scopeTreePos = graph.pipelineToLoopScope[pipeline]; + PipelineId const scopePl = graph.pltreeToPipeline[scopeTreePos]; + + ExecPipeline &rScopeExecPl = rExec.plData[scopePl]; + LGRN_ASSERT(rScopeExecPl.loopPipelinesLeft != 0); + -- rScopeExecPl.loopPipelinesLeft; + + if (rScopeExecPl.loopPipelinesLeft == 0) + { + // all loop pipelines parented to this are done + if ( ! rScopeExecPl.canceled ) + { + // Loop more + + uint32_t const descendants = graph.pltreeDescendantCounts[scopeTreePos]; + PipelineTreePos_t const lastPos = scopeTreePos + 1 + descendants; + + for (PipelineTreePos_t loopPos = scopeTreePos; loopPos < lastPos; ++loopPos) + { + PipelineId const loopPipeline = graph.pltreeToPipeline[loopPos]; + ExecPipeline &rLoopExecPl = rExec.plData[loopPipeline]; + + rExec.plAdvanceNext.set(std::size_t(loopPipeline)); + rExec.hasPlAdvanceOrLoop = true; + } + + rScopeExecPl.loopPipelinesLeft = 1 + descendants; + } + else + { + // Loop finished + + uint32_t const descendants = graph.pltreeDescendantCounts[scopeTreePos]; + PipelineTreePos_t const lastPos = scopeTreePos + 1 + descendants; + + for (PipelineTreePos_t loopPos = scopeTreePos; loopPos < lastPos; ++loopPos) + { + PipelineId const loopPipeline = graph.pltreeToPipeline[loopPos]; + ExecPipeline &rLoopExecPl = rExec.plData[loopPipeline]; + + rLoopExecPl.running = false; + rLoopExecPl.canceled = false; + rLoopExecPl.loop = false; + } + } + } } else { @@ -118,15 +316,14 @@ static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, E rExecPl.stage = lgrn::id_null(); rExecPl.running = false; rExecPl.canceled = false; - rExecPl.loop = false; } } -static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept { ExecPipeline &rExecPl = rExec.plData[pipeline]; - if ( ! rExecPl.running ) + if (rExecPl.stage == lgrn::id_null()) { return; } @@ -222,11 +419,11 @@ static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, Ex } } -static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) +static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept { ExecPipeline &rExecPl = rExec.plData[pipeline]; - if ( ! rExecPl.running ) + if (rExecPl.stage == lgrn::id_null()) { return; } @@ -282,235 +479,181 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe // all done. If there is none, then this pipeline may get stuck if nothing sets it dirty, // so set dirty right away. rExec.plAdvanceNext.set(std::size_t(pipeline)); - rExec.hasPlAdvance = true; + rExec.hasPlAdvanceOrLoop = true; } } +//----------------------------------------------------------------------------- -static void run_pipeline_recurse(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline, bool loop = false) -{ - - - //auto const children = ArrayView{fanout_view(graph.pipelineToFirstChild, graph.childPlToParent, pipeline)}; +// Pipeline utility - //for (PipelineId const plSub : children) - //{ - // run_pipeline_recurse(tasks, graph, rExec, PipelineId(plSub), loop); - //} -} - -static void cancel_stage(TaskGraph const& graph, ExecContext& rExec, AnyStageId const anystg) +static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) noexcept { - auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; - - for (TaskId task : runTasks) + auto const decrement_stage_dependencies = [&graph, &rExec] (AnyStageId const anystg) { - // Stages depend on this RunTask (reverse Stage-requires-Task) - for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) - { - PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; - StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); - ExecPipeline &rReqExecPl = rExec.plData[reqPl]; + auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; - if (rReqExecPl.stage == reqStg) + for (TaskId task : runTasks) + { + // Stages depend on this RunTask (reverse Stage-requires-Task) + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) { - LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); - -- rReqExecPl.ownStageReqTasksLeft; - pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); - } - } + PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); + ExecPipeline &rReqExecPl = rExec.plData[reqPl]; - // RunTask depends on stages (Task-requires-Stage) - for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) - { - ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; + if (rReqExecPl.stage == reqStg) + { + LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); + -- rReqExecPl.ownStageReqTasksLeft; + pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); + } + } - if (rReqExecPl.stage == req.reqStage) + // RunTask depends on stages (Task-requires-Stage) + for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) { - LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); - -- rReqExecPl.tasksReqOwnStageLeft; - pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); + ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; + + if (rReqExecPl.stage == req.reqStage) + { + LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); + -- rReqExecPl.tasksReqOwnStageLeft; + pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); + } } } - } -} - -void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) -{ - if (rExecPl.canceled) - { - return; // already cancelled - } - - int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); - - auto anystgInt = uint32_t(anystg_from(graph, pipeline, rExecPl.stage)); - for (auto stgInt = int(rExecPl.stage); stgInt < stageCount; ++stgInt, ++anystgInt) - { - cancel_stage(graph, rExec, AnyStageId(anystgInt)); - } - - rExecPl.canceled = true; -} - -void pipeline_cancel_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) -{ - -} + }; -void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept -{ - exec_log(rExec, ExecContext::EnqueueStart{}); + PipelineTreePos_t const treePos = graph.pipelineToPltree[pipeline]; - auto run_pipeline = [&graph, &rExec] (PipelineId const pipeline, bool const loop) + bool const skipDependencies = [&graph, &rExecPl, pipeline, treePos] () { - ExecPipeline &rExecPl = rExec.plData[pipeline]; - if (fanout_size(graph.pipelineToFirstAnystg, pipeline) != 0) + if (rExecPl.loop) { - rExecPl.running = true; - rExecPl.loop = loop; + PipelineTreePos_t const loopScope = graph.pipelineToLoopScope[pipeline]; - if (rExecPl.ownStageReqTasksLeft == 0) + bool const inNestedLoop = loopScope != lgrn::id_null() + && loopScope != treePos; + + if (inNestedLoop) { - rExec.plAdvance.set(std::size_t(pipeline)); - rExec.hasPlAdvance = true; + // Even if canceled, this pipeline may run again + return true; } } - }; + return false; + }(); - if (rExec.hasRequestRun) + uint32_t const descendants = graph.pltreeDescendantCounts[treePos]; + PipelineTreePos_t const lastPos = treePos + 1 + descendants; + + // Iterate descendents + for (PipelineTreePos_t cancelPos = treePos; cancelPos < lastPos; ++cancelPos) { - for (ExecPipeline const& rExecPl : rExec.plData) - { - LGRN_ASSERTM( ! rExecPl.running, "Running new pipelines while already running is not yet supported ROFL!"); - } + PipelineId const cancelPl = graph.pltreeToPipeline[cancelPos]; + ExecPipeline &rCancelExecPl = rExec.plData[cancelPl]; - for (PipelineInt const plInt : rExec.plRequestRun.ones()) + if ( ! rCancelExecPl.canceled && ! skipDependencies ) { - auto const pipeline = PipelineId(plInt); + int const stageCount = fanout_size(graph.pipelineToFirstAnystg, cancelPl); - PipelineTreePos_t const treePos = graph.pipelineToPltree[pipeline]; + int cancelStage = int(rCancelExecPl.stage) + 1; - if (treePos == lgrn::id_null()) - { - // Pipeline not in tree - run_pipeline(pipeline, tasks.m_pipelineControl[pipeline].loops); - } - else + auto anystgInt = uint32_t(anystg_from(graph, cancelPl, StageId(cancelStage))); + for (auto stgInt = cancelStage; stgInt < stageCount; ++stgInt, ++anystgInt) { - uint32_t const descendants = graph.pltreeDescendantCounts[treePos]; - PipelineTreePos_t const lastPos = treePos + 1 + descendants; - - uint32_t loopNextN = 0; - - // Iterate descendents - for (PipelineTreePos_t run = treePos; run < lastPos; ++run) - { - PipelineId const runPipeline = graph.pltreeToPipeline[run]; - - if (tasks.m_pipelineControl[runPipeline].loops) - { - // Loop this pipeline and all of its descendants - loopNextN = std::max(loopNextN, 1 + graph.pltreeDescendantCounts[run]); - } - - run_pipeline(runPipeline, loopNextN != 0); - - loopNextN -= (loopNextN != 0); - } + decrement_stage_dependencies(AnyStageId(anystgInt)); } } - rExec.plRequestRun.reset(); - rExec.hasRequestRun = false; - } + rCancelExecPl.canceled = true; + } +} - while (rExec.hasPlAdvance) +static inline void pipeline_try_advance(TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) noexcept +{ + if (pipeline_can_advance(graph, rExec, rExecPl)) { - exec_log(rExec, ExecContext::EnqueueCycle{}); - - rExec.hasPlAdvance = false; + rExec.plAdvance.set(std::size_t(pipeline)); + rExec.hasPlAdvanceOrLoop = true; + } +}; - for (PipelineInt const plInt : rExec.plAdvance.ones()) - { - pipeline_advance_stage(tasks, graph, rExec, PipelineId(plInt)); - } +//----------------------------------------------------------------------------- - for (PipelineInt const plInt : rExec.plAdvance.ones()) - { - pipeline_advance_reqs(tasks, graph, rExec, PipelineId(plInt)); - } +// Read-only checks - for (PipelineInt const plInt : rExec.plAdvance.ones()) - { - pipeline_advance_run(tasks, graph, rExec, PipelineId(plInt)); - } - - std::copy(rExec.plAdvanceNext.ints().begin(), - rExec.plAdvanceNext.ints().end(), - rExec.plAdvance .ints().begin()); - rExec.plAdvanceNext.reset(); - } +static constexpr bool pipeline_can_advance(TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept +{ + return rExecPl.ownStageReqTasksLeft == 0 // Tasks required by stage are done + && rExecPl.tasksReqOwnStageLeft == 0 // Not required by any tasks + && (rExecPl.tasksQueuedBlocked+rExecPl.tasksQueuedRun) == 0; // Tasks done - exec_log(rExec, ExecContext::EnqueueEnd{}); } -void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TaskActions actions) noexcept +/** + * @brief Check if a pipeline 'sees' another pipeline is enclosed within a loop + */ +static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, ArgsForIsPipelineInLoop const args) noexcept { - LGRN_ASSERT(rExec.tasksQueuedRun.contains(task)); - rExec.tasksQueuedRun.erase(task); - - exec_log(rExec, ExecContext::CompleteTask{task}); + PipelineTreePos_t const insideLoopScopePos = graph.pipelineToLoopScope[args.insideLoop]; - auto const [pipeline, stage] = tasks.m_taskRunOn[task]; - ExecPipeline &rExecPl = rExec.plData[pipeline]; + if (insideLoopScopePos == lgrn::id_null()) + { + return false; // insideLoop is in the root, not inside a loop lol + } - -- rExecPl.tasksQueuedRun; + PipelineTreePos_t const viewedFromScopePos = graph.pipelineToLoopScope[args.viewedFrom]; - pipeline_try_advance(graph, rExec, rExecPl, pipeline); - - // Handle stages requiring this task - for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) + if (insideLoopScopePos == viewedFromScopePos) { - PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; - StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); - ExecPipeline &rReqExecPl = rExec.plData[reqPl]; - - if (rReqExecPl.stage == reqStg) - { - -- rReqExecPl.ownStageReqTasksLeft; + return false; // Both pipelines are in the same loop scope + } + else if (viewedFromScopePos == lgrn::id_null()) + { + return true; // viewedFrom is in the root, and insideLoop is enclosed in a loop (which has to be enclosed in root of course) + } + else + { + // Check tree if insideLoopScopePos is a descendent of viewedFromScopePos + PipelineTreePos_t const ancestor = viewedFromScopePos; + PipelineTreePos_t const ancestorLastChild = viewedFromScopePos + 1 + graph.pltreeDescendantCounts[viewedFromScopePos]; + PipelineTreePos_t const descendent = insideLoopScopePos; - pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); - } - else - { - LGRN_ASSERTMV(int(rReqExecPl.stage) < int(reqStg) && rReqExecPl.stage != lgrn::id_null(), - "Stage-requires-Task means that rReqExecPl.stage cannot advance any further than reqStg until task completes.", - int(task), int(rReqExecPl.stage), int(reqStg)); - } + return (ancestor < descendent) && (descendent < ancestorLastChild); } +} - // Handle this task requiring stages - for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) - { - ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; +//----------------------------------------------------------------------------- - LGRN_ASSERTMV(rReqExecPl.stage == req.reqStage, - "Task-requires-Stage means this task should have not run unless the stage is selected", - int(task), int(rReqExecPl.stage), int(req.reqStage)); +// Minor utility - -- rReqExecPl.tasksReqOwnStageLeft; +void exec_resize(Tasks const& tasks, ExecContext &rOut) +{ + std::size_t const maxTasks = tasks.m_taskIds.capacity(); + std::size_t const maxPipeline = tasks.m_pipelineIds.capacity(); - pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); - } + rOut.tasksQueuedRun .reserve(maxTasks); + rOut.tasksQueuedBlocked.reserve(maxTasks); + rOut.plData.resize(maxPipeline); + bitvector_resize(rOut.plAdvance, maxPipeline); + bitvector_resize(rOut.plAdvanceNext, maxPipeline); + bitvector_resize(rOut.plRequestRun, maxPipeline); +} - // Handle actions +void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) +{ + exec_resize(tasks, rOut); +} - if (actions & TaskAction::Cancel) +static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) noexcept +{ + if (rExec.doLogging) { - pipeline_cancel(tasks, graph, rExec, rExecPl, pipeline); + rExec.logMsg.push_back(msg); } - } + } // namespace osp diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 74b723de..9eaf05f0 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -46,6 +46,8 @@ struct ExecPipeline int tasksReqOwnStageLeft{0}; int ownStageReqTasksLeft{0}; + int loopPipelinesLeft {0}; + StageId stage { lgrn::id_null() }; bool tasksQueueDone { false }; @@ -60,6 +62,12 @@ struct BlockedTask PipelineId pipeline; }; +struct LoopRequestRun +{ + PipelineId pipeline; + PipelineTreePos_t treePos; +}; + /** * @brief */ @@ -72,12 +80,15 @@ struct ExecContext BitVector_t plAdvance; BitVector_t plAdvanceNext; - bool hasPlAdvance {false}; + bool hasPlAdvanceOrLoop {false}; BitVector_t plRequestRun; + std::vector requestLoop; bool hasRequestRun {false}; + + // 'logging' struct EnqueueStart { }; @@ -147,13 +158,15 @@ void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); void exec_resize(Tasks const& tasks, ExecContext &rOut); -void pipeline_run(ExecContext &rExec, PipelineId pipeline); - -void pipeline_cancel_optionals(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline); +inline void exec_request_run(ExecContext &rExec, PipelineId pipeline) noexcept +{ + rExec.plRequestRun.set(std::size_t(pipeline)); + rExec.hasRequestRun = true; +} void pipeline_cancel_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline); -void enqueue_dirty(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; +void exec_update(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId task, TaskActions actions) noexcept; diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index f1a52379..dd10faad 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -179,6 +179,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView out.pltreeDescendantCounts .resize(treeSize, 0); out.pltreeToPipeline .resize(treeSize, lgrn::id_null()); out.pipelineToPltree .resize(maxPipelines, lgrn::id_null()); + out.pipelineToLoopScope .resize(maxPipelines, lgrn::id_null()); // 5. Calculate one-to-many partitions @@ -330,26 +331,29 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView // 7. Build Pipeline Tree - auto const add_subtree = [&] (auto const& self, PipelineId const root, PipelineId const firstChild, PipelineTreePos_t const pos) -> uint32_t + auto const add_subtree = [&] (auto const& self, PipelineId const root, PipelineId const firstChild, PipelineTreePos_t const loopScope, PipelineTreePos_t const pos) -> uint32_t { - out.pltreeToPipeline[pos] = root; - out.pipelineToPltree[root] = pos; + out.pltreeToPipeline[pos] = root; + out.pipelineToPltree[root] = pos; + out.pipelineToLoopScope[root] = loopScope; uint32_t descendantCount = 0; PipelineId child = firstChild; - PipelineTreePos_t childPos = pos; + PipelineTreePos_t childPos = pos + 1; while (child != lgrn::id_null()) { - PipelineCounts const& rChildCounts = plCounts[child]; + PipelineCounts const& rChildCounts = plCounts[child]; + bool const childLoops = tasks.m_pipelineControl[child].loops; + PipelineTreePos_t childLoopScope = childLoops ? childPos : loopScope; - ++ childPos; - uint32_t const childDescendantCount = self(self, child, rChildCounts.firstChild, childPos); + uint32_t const childDescendantCount = self(self, child, rChildCounts.firstChild, childLoopScope, childPos); descendantCount += 1 + childDescendantCount; child = rChildCounts.sibling; + ++ childPos; } out.pltreeDescendantCounts[pos] = descendantCount; @@ -371,7 +375,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView PipelineCounts const& rRootCounts = plCounts[pipeline]; - uint32_t const rootDescendantCount = add_subtree(add_subtree, pipeline, rRootCounts.firstChild, rootPos); + uint32_t const rootDescendantCount = add_subtree(add_subtree, pipeline, rRootCounts.firstChild, lgrn::id_null(), rootPos); rootPos += 1 + rootDescendantCount; } diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 759fd64b..589ea381 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -195,6 +195,7 @@ struct TaskGraph KeyedVec pltreeDescendantCounts; KeyedVec pltreeToPipeline; KeyedVec pipelineToPltree; + KeyedVec pipelineToLoopScope; // not yet used lgrn::IntArrayMultiMap taskAcquire; /// Tasks acquire (n) Semaphores @@ -314,7 +315,7 @@ struct PipelineDef PipelineInfo::stage_type_t m_type { PipelineInfo::stage_type_family_t::value }; - PipelineId m_value { lgrn::id_null() }; + PipelineId m_value { PipelineId(69) }; }; using PipelineDefBlank_t = PipelineDef; From f4960a4d5d4a743ab29694cb65bd5197d3d352d5 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sat, 5 Aug 2023 05:03:23 -0700 Subject: [PATCH 27/35] Implement Pipeline Nested Loops --- src/osp/tasks/execute.cpp | 523 +++++++++++++++++++++++--------------- src/osp/tasks/execute.h | 95 +++++-- src/osp/tasks/tasks.cpp | 9 +- src/osp/tasks/tasks.h | 3 +- test/tasks/main.cpp | 242 ++++++++++++++++-- 5 files changed, 612 insertions(+), 260 deletions(-) diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 84e4e8c7..b303416c 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -32,15 +32,19 @@ namespace osp static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) noexcept; -static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept; +static void pipeline_run_root(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) noexcept; -static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept; +static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, bool useAdvanceNext, PipelineId const pipeline, PipelineTreePos_t treePos, uint32_t descendents, bool isLoopScope, bool insideLoopScope); -static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept; +static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) noexcept; -static constexpr bool pipeline_can_advance(TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept; +static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) noexcept; -static inline void pipeline_try_advance(TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) noexcept; +static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) noexcept; + +static constexpr bool pipeline_can_advance(Tasks const& tasks, TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept; + +static inline void pipeline_try_advance(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) noexcept; static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) noexcept; @@ -50,7 +54,16 @@ struct ArgsForIsPipelineInLoop PipelineId insideLoop; }; -static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, ArgsForIsPipelineInLoop const args) noexcept; +static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext const& exec, ArgsForIsPipelineInLoop const args) noexcept; + +struct ArgsForSubtreeForEach +{ + PipelineTreePos_t root; + bool includeRoot; +}; + +template +static void subtree_for_each(ArgsForSubtreeForEach args, TaskGraph const& graph, ExecContext const& rExec, FUNC_T&& func); //----------------------------------------------------------------------------- @@ -58,22 +71,7 @@ static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, Args void exec_update(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept { - auto const run_pipeline = [&graph, &rExec] (ExecPipeline& rExecPl, PipelineId const pipeline, bool const loop) - { - if (fanout_size(graph.pipelineToFirstAnystg, pipeline) != 0) - { - rExecPl.running = true; - rExecPl.loop = loop; - - LGRN_ASSERT(rExecPl.ownStageReqTasksLeft == 0); - { - rExec.plAdvance.set(std::size_t(pipeline)); - rExec.hasPlAdvanceOrLoop = true; - } - } - }; - - exec_log(rExec, ExecContext::EnqueueStart{}); + exec_log(rExec, ExecContext::UpdateStart{}); if (rExec.hasRequestRun) { @@ -84,41 +82,7 @@ void exec_update(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) for (PipelineInt const plInt : rExec.plRequestRun.ones()) { - auto const pipeline = PipelineId(plInt); - - PipelineTreePos_t const treePos = graph.pipelineToPltree[pipeline]; - - if (treePos == lgrn::id_null()) - { - // Pipeline not in tree - run_pipeline(rExec.plData[pipeline], pipeline, tasks.m_pipelineControl[pipeline].loops); - } - else - { - uint32_t const descendants = graph.pltreeDescendantCounts[treePos]; - PipelineTreePos_t const lastPos = treePos + 1 + descendants; - - uint32_t loopNextN = 0; - - for (PipelineTreePos_t runPos = treePos; runPos < lastPos; ++runPos) - { - PipelineId const runPipeline = graph.pltreeToPipeline[runPos]; - ExecPipeline &rRunExecPl = rExec.plData[runPipeline]; - - if (tasks.m_pipelineControl[runPipeline].loops) - { - uint32_t const runDescendants = graph.pltreeDescendantCounts[runPos]; - rRunExecPl.loopPipelinesLeft = 1 + runDescendants; - - // Loop this pipeline and all of its descendants - loopNextN = std::max(loopNextN, 1 + runDescendants); - } - - run_pipeline(rRunExecPl, runPipeline, loopNextN != 0); - - loopNextN -= (loopNextN != 0); - } - } + pipeline_run_root(tasks, graph, rExec, PipelineId(plInt)); } rExec.plRequestRun.reset(); rExec.hasRequestRun = false; @@ -127,17 +91,16 @@ void exec_update(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) while (rExec.hasPlAdvanceOrLoop) { - exec_log(rExec, ExecContext::EnqueueCycle{}); + exec_log(rExec, ExecContext::UpdateCycle{}); rExec.hasPlAdvanceOrLoop = false; for (auto const [pipeline, treePos] : rExec.requestLoop) { ExecPipeline &rExecPl = rExec.plData[pipeline]; - LGRN_ASSERT(rExecPl.loopPipelinesLeft == 0); - - + LGRN_ASSERT(rExecPl.loopChildrenLeft == 0); } + rExec.requestLoop.clear(); for (PipelineInt const plInt : rExec.plAdvance.ones()) @@ -161,7 +124,7 @@ void exec_update(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) rExec.plAdvanceNext.reset(); } - exec_log(rExec, ExecContext::EnqueueEnd{}); + exec_log(rExec, ExecContext::UpdateEnd{}); } void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, TaskId const task, TaskActions actions) noexcept @@ -176,7 +139,7 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe -- rExecPl.tasksQueuedRun; - pipeline_try_advance(graph, rExec, rExecPl, pipeline); + pipeline_try_advance(tasks, graph, rExec, rExecPl, pipeline); if (actions & TaskAction::Cancel) { @@ -195,14 +158,15 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe if (rReqExecPl.stage == reqStg) { - if ( ! (rExecPl.loop && is_pipeline_in_loop(tasks, graph, {.viewedFrom = reqPl, .insideLoop = pipeline})) || rExecPl.canceled ) + // True if 'reqPl' sees that 'pipeline' is inside a loop, and may run more times + bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = reqPl, .insideLoop = pipeline}); + + if ( ( ! loopsRelative ) || rExecPl.canceled ) { -- rReqExecPl.ownStageReqTasksLeft; - pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); + pipeline_try_advance(tasks, graph, rExec, rReqExecPl, reqPl); } - // else: This pipeline is enclosed in a loop relative to dependency. This task may run - // more times. } else { @@ -221,11 +185,13 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe "Task-requires-Stage means this task should have not run unless the stage is selected", int(task), int(rReqExecPl.stage), int(req.reqStage)); - if ( ! (rExecPl.loop && is_pipeline_in_loop(tasks, graph, {.viewedFrom = req.reqPipeline, .insideLoop = pipeline})) || rExecPl.canceled ) + bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = req.reqPipeline, .insideLoop = pipeline}); + + if ( ( ! loopsRelative ) || rExecPl.canceled ) { -- rReqExecPl.tasksReqOwnStageLeft; - pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); + pipeline_try_advance(tasks, graph, rExec, rReqExecPl, req.reqPipeline); } // else: This pipeline is enclosed in a loop relative to dependency. This task may run // more times. @@ -236,86 +202,192 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe // Major steps -static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept +static void pipeline_run_root(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept { + exec_log(rExec, ExecLog::ExternalRunRequest{pipeline}); + + PipelineTreePos_t const treePos = graph.pipelineToPltree[pipeline]; + + bool const isLoopScope = tasks.m_pipelineControl[pipeline].isLoopScope; + + uint32_t const descendents = (treePos != lgrn::id_null()) + ? graph.pltreeDescendantCounts[treePos] + : 0; + + pipeline_run(tasks, graph, rExec, false, pipeline, treePos, descendents, isLoopScope, false); +} + +static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, bool const rerunLoop, PipelineId const pipeline, PipelineTreePos_t const treePos, uint32_t const descendents, bool const isLoopScope, bool const insideLoopScope) +{ + auto const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); ExecPipeline &rExecPl = rExec.plData[pipeline]; - LGRN_ASSERT(pipeline_can_advance(graph, rExec, rExecPl)); - // * rExecPl.ownStageReqTasksLeft == 0; - // * rExecPl.tasksReqOwnStageLeft == 0; + if (rExecPl.stage != lgrn::id_null()) + { + return 0; + } - int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); - LGRN_ASSERTM(stageCount != 0, "Pipelines with 0 stages shouldn't be running"); + if (stageCount != 0) + { + rExecPl.running = true; + rExecPl.loop = isLoopScope || insideLoopScope; - bool const justStarting = rExecPl.stage == lgrn::id_null(); + if (rerunLoop) + { + rExec.plAdvanceNext.set(std::size_t(pipeline)); + exec_log(rExec, ExecContext::PipelineLoop{pipeline}); + } + else + { + rExec.plAdvance.set(std::size_t(pipeline)); + exec_log(rExec, ExecContext::PipelineRun{pipeline}); + } - auto const nextStage = StageId( justStarting ? 0 : (int(rExecPl.stage)+1) ); + rExec.hasPlAdvanceOrLoop = true; + } - rExecPl.tasksQueueDone = false; + if (descendents == 0) + { + return 0; + } - if (nextStage != StageId(stageCount)) + PipelineTreePos_t const childPosLast = treePos + 1 + descendents; + PipelineTreePos_t childPos = treePos + 1; + + int childCount = 0; + + while (childPos != childPosLast) { - // Proceed to next stage since its valid - rExecPl.stage = nextStage; + uint32_t const childDescendents = graph.pltreeDescendantCounts[childPos]; + + PipelineId const childPl = graph.pltreeToPipeline[childPos]; + bool const childIsLoopScope = tasks.m_pipelineControl[childPl].isLoopScope; + + pipeline_run(tasks, graph, rExec, rerunLoop, childPl, childPos, childDescendents, childIsLoopScope, isLoopScope || insideLoopScope); + + ++ childCount; + childPos += 1 + childDescendents; } - else if ( rExecPl.loop ) + + if (isLoopScope) { - // Loop done - rExecPl.stage = lgrn::id_null(); + rExecPl.loopChildrenLeft = childCount; + } - PipelineTreePos_t const scopeTreePos = graph.pipelineToLoopScope[pipeline]; - PipelineId const scopePl = graph.pltreeToPipeline[scopeTreePos]; + return childCount; +} + + +static void loop_scope_done(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline, PipelineTreePos_t const treePos) +{ + + if ( ! rExecPl.canceled ) + { + // Loop more - ExecPipeline &rScopeExecPl = rExec.plData[scopePl]; - LGRN_ASSERT(rScopeExecPl.loopPipelinesLeft != 0); - -- rScopeExecPl.loopPipelinesLeft; + uint32_t const descendents = graph.pltreeDescendantCounts[treePos]; + pipeline_run(tasks, graph, rExec, true, pipeline, treePos, descendents, true, /* dont-care */ false); + } + else + { + // Loop finished - if (rScopeExecPl.loopPipelinesLeft == 0) + subtree_for_each( + {.root = treePos, .includeRoot = true}, graph, rExec, + [&tasks, &graph, &rExec] + (PipelineTreePos_t const loopPos, PipelineId const loopPipeline, uint32_t const descendants) { - // all loop pipelines parented to this are done - if ( ! rScopeExecPl.canceled ) - { - // Loop more + ExecPipeline &rLoopExecPl = rExec.plData[loopPipeline]; - uint32_t const descendants = graph.pltreeDescendantCounts[scopeTreePos]; - PipelineTreePos_t const lastPos = scopeTreePos + 1 + descendants; + rLoopExecPl.running = false; + rLoopExecPl.canceled = false; + rLoopExecPl.loop = false; - for (PipelineTreePos_t loopPos = scopeTreePos; loopPos < lastPos; ++loopPos) - { - PipelineId const loopPipeline = graph.pltreeToPipeline[loopPos]; - ExecPipeline &rLoopExecPl = rExec.plData[loopPipeline]; + exec_log(rExec, ExecContext::PipelineLoopFinish{loopPipeline}); + }); - rExec.plAdvanceNext.set(std::size_t(loopPipeline)); - rExec.hasPlAdvanceOrLoop = true; - } + // If this is a nested loop, decrement parent loop scope's loopChildrenLeft - rScopeExecPl.loopPipelinesLeft = 1 + descendants; - } - else - { - // Loop finished + PipelineId const parentScopePl = tasks.m_pipelineParents[pipeline]; + if (parentScopePl == lgrn::id_null()) + { + return; // Loop is in the root + } - uint32_t const descendants = graph.pltreeDescendantCounts[scopeTreePos]; - PipelineTreePos_t const lastPos = scopeTreePos + 1 + descendants; + PipelineTreePos_t const parentScopeTreePos = graph.pipelineToLoopScope[parentScopePl]; + if (parentScopeTreePos == lgrn::id_null()) + { + return; // Parent does not loop + } - for (PipelineTreePos_t loopPos = scopeTreePos; loopPos < lastPos; ++loopPos) - { - PipelineId const loopPipeline = graph.pltreeToPipeline[loopPos]; - ExecPipeline &rLoopExecPl = rExec.plData[loopPipeline]; + ExecPipeline &rParentScopeExecPl = rExec.plData[parentScopePl]; - rLoopExecPl.running = false; - rLoopExecPl.canceled = false; - rLoopExecPl.loop = false; - } - } + LGRN_ASSERT(rParentScopeExecPl.loopChildrenLeft != 0); + -- rParentScopeExecPl.loopChildrenLeft; + + if (rExecPl.loopChildrenLeft == 0) + { + loop_scope_done(tasks, graph, rExec, rParentScopeExecPl, parentScopePl, parentScopeTreePos); } } +} + +static void loop_done(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) +{ + rExecPl.stage = lgrn::id_null(); + + PipelineTreePos_t scopeTreePos = graph.pipelineToLoopScope[pipeline]; + PipelineId scopePl = graph.pltreeToPipeline[scopeTreePos]; + ExecPipeline &rScopeExecPl = rExec.plData[scopePl]; + + if (scopePl != pipeline) // if not a loopscope itself + { + LGRN_ASSERT(rScopeExecPl.loopChildrenLeft != 0); + -- rScopeExecPl.loopChildrenLeft; + } + + if (rScopeExecPl.loopChildrenLeft == 0 && rScopeExecPl.stage == lgrn::id_null()) + { + loop_scope_done(tasks, graph, rExec, rScopeExecPl, scopePl, scopeTreePos); + } +} + +static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId const pipeline) noexcept +{ + ExecPipeline &rExecPl = rExec.plData[pipeline]; + + LGRN_ASSERT(pipeline_can_advance(tasks, graph, rExec, rExecPl)); + // * rExecPl.ownStageReqTasksLeft == 0; + // * rExecPl.tasksReqOwnStageLeft == 0; + + int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); + LGRN_ASSERTM(stageCount != 0, "Pipelines with 0 stages shouldn't be running"); + + bool const justStarting = rExecPl.stage == lgrn::id_null(); + + auto const nextStage = StageId( justStarting ? 0 : (int(rExecPl.stage)+1) ); + + rExecPl.tasksQueueDone = false; + + if (nextStage != StageId(stageCount)) + { + exec_log(rExec, ExecContext::StageChange{pipeline, rExecPl.stage, nextStage}); + + // Proceed to next stage since its valid + rExecPl.stage = nextStage; + } + else if ( rExecPl.loop ) + { + loop_done(tasks, graph, rExec, rExecPl, pipeline); + } else { // Finished running rExecPl.stage = lgrn::id_null(); rExecPl.running = false; rExecPl.canceled = false; + + exec_log(rExec, ExecContext::PipelineFinish{pipeline}); } } @@ -457,23 +529,35 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe } } - if (reqStagesLeft == 0) + bool const blocked = reqStagesLeft != 0; + + if (blocked) + { + rExec.tasksQueuedBlocked.emplace(task, BlockedTask{reqStagesLeft, pipeline}); + ++ rExecPl.tasksQueuedBlocked; + } + else { - // Task can run right away rExec.tasksQueuedRun.emplace(task); ++ rExecPl.tasksQueuedRun; } - else + + exec_log(rExec, ExecContext::EnqueueTask{pipeline, rExecPl.stage, task, blocked}); + if (rExec.doLogging) { - rExec.tasksQueuedBlocked.emplace(task, BlockedTask{reqStagesLeft, pipeline}); - ++ rExecPl.tasksQueuedBlocked; + for (TaskRequiresStage const& req : taskreqstageView) + { + ExecPipeline const &rReqPlData = rExec.plData[req.reqPipeline]; + + exec_log(rExec, ExecContext::EnqueueTaskReq{req.reqPipeline, req.reqStage, rReqPlData.stage == req.reqStage}); + } } } } rExecPl.tasksQueueDone = true; - if (noTasksRun && pipeline_can_advance(graph, rExec, rExecPl)) + if (noTasksRun && pipeline_can_advance(tasks, graph, rExec, rExecPl)) { // No tasks to run. RunTasks are responsible for setting this pipeline dirty once they're // all done. If there is none, then this pipeline may get stuck if nothing sets it dirty, @@ -489,91 +573,77 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) noexcept { - auto const decrement_stage_dependencies = [&graph, &rExec] (AnyStageId const anystg) - { - auto const runTasks = ArrayView{fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)}; - - for (TaskId task : runTasks) - { - // Stages depend on this RunTask (reverse Stage-requires-Task) - for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) - { - PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; - StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); - ExecPipeline &rReqExecPl = rExec.plData[reqPl]; - - if (rReqExecPl.stage == reqStg) - { - LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); - -- rReqExecPl.ownStageReqTasksLeft; - pipeline_try_advance(graph, rExec, rReqExecPl, reqPl); - } - } - - // RunTask depends on stages (Task-requires-Stage) - for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) - { - ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; - - if (rReqExecPl.stage == req.reqStage) - { - LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); - -- rReqExecPl.tasksReqOwnStageLeft; - pipeline_try_advance(graph, rExec, rReqExecPl, req.reqPipeline); - } - } - } - }; - PipelineTreePos_t const treePos = graph.pipelineToPltree[pipeline]; - bool const skipDependencies = [&graph, &rExecPl, pipeline, treePos] () + subtree_for_each( + {.root = treePos, .includeRoot = true}, graph, rExec, + [&tasks, &graph, &rExec, &rExecPl, pipeline] + (PipelineTreePos_t const cancelPos, PipelineId const cancelPl, uint32_t const descendants) { - if (rExecPl.loop) - { - PipelineTreePos_t const loopScope = graph.pipelineToLoopScope[pipeline]; - - bool const inNestedLoop = loopScope != lgrn::id_null() - && loopScope != treePos; - - if (inNestedLoop) - { - // Even if canceled, this pipeline may run again - return true; - } - } - return false; - }(); - - uint32_t const descendants = graph.pltreeDescendantCounts[treePos]; - PipelineTreePos_t const lastPos = treePos + 1 + descendants; - - // Iterate descendents - for (PipelineTreePos_t cancelPos = treePos; cancelPos < lastPos; ++cancelPos) - { - PipelineId const cancelPl = graph.pltreeToPipeline[cancelPos]; ExecPipeline &rCancelExecPl = rExec.plData[cancelPl]; - if ( ! rCancelExecPl.canceled && ! skipDependencies ) + + if ( ! rCancelExecPl.canceled ) { - int const stageCount = fanout_size(graph.pipelineToFirstAnystg, cancelPl); + rCancelExecPl.canceled = true; - int cancelStage = int(rCancelExecPl.stage) + 1; + auto const stageCount = int(fanout_size(graph.pipelineToFirstAnystg, cancelPl)); + auto const stagesAhead = int(rCancelExecPl.stage) + 1; - auto anystgInt = uint32_t(anystg_from(graph, cancelPl, StageId(cancelStage))); - for (auto stgInt = cancelStage; stgInt < stageCount; ++stgInt, ++anystgInt) + auto anystgInt = uint32_t(anystg_from(graph, cancelPl, StageId(stagesAhead))); + for (auto stgInt = stagesAhead; stgInt < stageCount; ++stgInt, ++anystgInt) { - decrement_stage_dependencies(AnyStageId(anystgInt)); + for (TaskId task : fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, AnyStageId(anystgInt))) + { + // Stages depend on this RunTask (reverse Stage-requires-Task) + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) + { + PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); + ExecPipeline &rReqExecPl = rExec.plData[reqPl]; + + if (rReqExecPl.stage == reqStg) + { + // True if 'reqPl' sees that 'pipeline' is inside a loop, and may run more times + bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = reqPl, .insideLoop = pipeline}); + + if ( ! loopsRelative ) + { + LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); + -- rReqExecPl.ownStageReqTasksLeft; + pipeline_try_advance(tasks, graph, rExec, rReqExecPl, reqPl); + } + } + } + + // RunTask depends on stages (Task-requires-Stage) + for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) + { + ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; + + if (rReqExecPl.stage == req.reqStage) + { + bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = req.reqPipeline, .insideLoop = pipeline}); + + if ( ! loopsRelative ) + { + LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); + -- rReqExecPl.tasksReqOwnStageLeft; + pipeline_try_advance(tasks, graph, rExec, rReqExecPl, req.reqPipeline); + } + } + } + } } } - rCancelExecPl.canceled = true; - } + exec_log(rExec, ExecContext::PipelineCancel{cancelPl, rCancelExecPl.stage}); + }); } -static inline void pipeline_try_advance(TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) noexcept +static inline void pipeline_try_advance(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) noexcept { - if (pipeline_can_advance(graph, rExec, rExecPl)) + if (pipeline_can_advance(tasks, graph, rExec, rExecPl)) { rExec.plAdvance.set(std::size_t(pipeline)); rExec.hasPlAdvanceOrLoop = true; @@ -584,7 +654,7 @@ static inline void pipeline_try_advance(TaskGraph const& graph, ExecContext &rEx // Read-only checks -static constexpr bool pipeline_can_advance(TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept +static constexpr bool pipeline_can_advance(Tasks const& tasks, TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept { return rExecPl.ownStageReqTasksLeft == 0 // Tasks required by stage are done && rExecPl.tasksReqOwnStageLeft == 0 // Not required by any tasks @@ -593,9 +663,9 @@ static constexpr bool pipeline_can_advance(TaskGraph const& graph, ExecContext c } /** - * @brief Check if a pipeline 'sees' another pipeline is enclosed within a loop + * @brief Check if a pipeline 'sees' another pipeline is enclosed within a non-cancelled loop */ -static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, ArgsForIsPipelineInLoop const args) noexcept +static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext const& exec, ArgsForIsPipelineInLoop const args) noexcept { PipelineTreePos_t const insideLoopScopePos = graph.pipelineToLoopScope[args.insideLoop]; @@ -612,7 +682,24 @@ static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, Args } else if (viewedFromScopePos == lgrn::id_null()) { - return true; // viewedFrom is in the root, and insideLoop is enclosed in a loop (which has to be enclosed in root of course) + // viewedFrom is in the root, and insideLoop is enclosed in a loop. + + if (exec.plData[args.insideLoop].canceled) + { + if (exec.plData[graph.pltreeToPipeline[insideLoopScopePos]].canceled) + { + return false; // insideLoop is canceled and will not loop again + } + else + { + return true; // insideLoop itself is canceled, but is parented to a non-canceled + // loop scope. It will loop more times. + } + } + else // insideLoop is canceled + { + return true; // insideLoop is not canceled and will loop more + } } else { @@ -621,7 +708,31 @@ static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, Args PipelineTreePos_t const ancestorLastChild = viewedFromScopePos + 1 + graph.pltreeDescendantCounts[viewedFromScopePos]; PipelineTreePos_t const descendent = insideLoopScopePos; - return (ancestor < descendent) && (descendent < ancestorLastChild); + bool const isDescendent = (ancestor < descendent) && (descendent < ancestorLastChild); + + if (isDescendent) + { + if (exec.plData[args.insideLoop].canceled) + { + if (exec.plData[graph.pltreeToPipeline[insideLoopScopePos]].canceled) + { + return false; // insideLoop is canceled and will not loop again + } + else + { + return true; // insideLoop is in a nested loop and is canceled, but is parented + // to a non-canceled loop scope. It will loop more times. + } + } + else + { + return true; // insideLoop is in a nested loop and is not canceled + } + } + else + { + return false; // viewedFrom and insideLoop are unrelated + } } } @@ -655,5 +766,17 @@ static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) noexcept } } +template +static void subtree_for_each(ArgsForSubtreeForEach args, TaskGraph const& graph, ExecContext const& rExec, FUNC_T&& func) +{ + uint32_t const descendants = graph.pltreeDescendantCounts[args.root]; + PipelineTreePos_t const lastPos = args.root + 1 + descendants; + PipelineTreePos_t const firstPos = args.includeRoot ? args.root : (args.root+1); + + for (PipelineTreePos_t pos = firstPos; pos < lastPos; ++pos) + { + std::invoke(func, pos, graph.pltreeToPipeline[pos], descendants); + } +} } // namespace osp diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 9eaf05f0..18991d1e 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -46,7 +46,7 @@ struct ExecPipeline int tasksReqOwnStageLeft{0}; int ownStageReqTasksLeft{0}; - int loopPipelinesLeft {0}; + int loopChildrenLeft {0}; StageId stage { lgrn::id_null() }; @@ -54,6 +54,7 @@ struct ExecPipeline bool loop { false }; bool running { false }; bool canceled { false }; + bool triggered { false }; }; struct BlockedTask @@ -68,32 +69,41 @@ struct LoopRequestRun PipelineTreePos_t treePos; }; + /** - * @brief + * @brief The ExecLog struct */ -struct ExecContext +struct ExecLog { - KeyedVec plData; - - entt::basic_sparse_set tasksQueuedRun; - entt::basic_storage tasksQueuedBlocked; - - BitVector_t plAdvance; - BitVector_t plAdvanceNext; - bool hasPlAdvanceOrLoop {false}; - - BitVector_t plRequestRun; - std::vector requestLoop; - bool hasRequestRun {false}; + struct UpdateStart { }; + struct UpdateCycle { }; + struct UpdateEnd { }; + struct PipelineRun + { + PipelineId pipeline; + }; + struct PipelineFinish + { + PipelineId pipeline; + }; + struct PipelineCancel + { + PipelineId pipeline; + StageId stage; + }; - // 'logging' + struct PipelineLoop + { + PipelineId pipeline; + }; - struct EnqueueStart { }; - struct EnqueueCycle { }; - struct EnqueueEnd { }; + struct PipelineLoopFinish + { + PipelineId pipeline; + }; struct StageChange { @@ -114,6 +124,7 @@ struct ExecContext { PipelineId pipeline; StageId stage; + bool satisfied; }; struct UnblockTask @@ -126,22 +137,49 @@ struct ExecContext TaskId task; }; - struct CompleteTaskTrigger - { - PipelineId pipeline; - StageId stage; - }; - - struct ExternalTrigger + struct ExternalRunRequest { PipelineId pipeline; - StageId stage; }; - using LogMsg_t = std::variant; + using LogMsg_t = std::variant< + UpdateStart, + UpdateCycle, + UpdateEnd, + PipelineRun, + PipelineFinish, + PipelineCancel, + PipelineLoop, + PipelineLoopFinish, + StageChange, + EnqueueTask, + EnqueueTaskReq, + UnblockTask, + CompleteTask, + ExternalRunRequest>; std::vector logMsg; bool doLogging{true}; +}; + + +/** + * @brief State for executing Tasks and TaskGraph + */ +struct ExecContext : public ExecLog +{ + KeyedVec plData; + + entt::basic_sparse_set tasksQueuedRun; + entt::basic_storage tasksQueuedBlocked; + + BitVector_t plAdvance; + BitVector_t plAdvanceNext; + bool hasPlAdvanceOrLoop {false}; + + BitVector_t plRequestRun; + std::vector requestLoop; + bool hasRequestRun {false}; // TODO: Consider multithreading. something something work stealing... // * Allow multiple threads to search for and execute tasks. Atomic access @@ -154,6 +192,7 @@ struct ExecContext }; // struct ExecContext + void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); void exec_resize(Tasks const& tasks, ExecContext &rOut); diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index dd10faad..d8452e62 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -333,9 +333,12 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView auto const add_subtree = [&] (auto const& self, PipelineId const root, PipelineId const firstChild, PipelineTreePos_t const loopScope, PipelineTreePos_t const pos) -> uint32_t { + bool const rootLoops = tasks.m_pipelineControl[root].isLoopScope; + PipelineTreePos_t newLoopScope = rootLoops ? pos : loopScope; + out.pltreeToPipeline[pos] = root; out.pipelineToPltree[root] = pos; - out.pipelineToLoopScope[root] = loopScope; + out.pipelineToLoopScope[root] = newLoopScope; uint32_t descendantCount = 0; @@ -346,10 +349,8 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView while (child != lgrn::id_null()) { PipelineCounts const& rChildCounts = plCounts[child]; - bool const childLoops = tasks.m_pipelineControl[child].loops; - PipelineTreePos_t childLoopScope = childLoops ? childPos : loopScope; - uint32_t const childDescendantCount = self(self, child, rChildCounts.firstChild, childLoopScope, childPos); + uint32_t const childDescendantCount = self(self, child, rChildCounts.firstChild, newLoopScope, childPos); descendantCount += 1 + childDescendantCount; child = rChildCounts.sibling; diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 589ea381..a5b6dc57 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -78,7 +78,8 @@ struct PipelineInfo struct PipelineControl { - bool loops; + StageId trigger { lgrn::id_null() }; + bool isLoopScope { false }; }; struct TplTaskPipelineStage diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 4a0f6156..bf5b4dba 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -68,7 +68,7 @@ void randomized_singlethreaded_execute(Tasks const& tasks, TaskGraph const& grap complete_task(tasks, graph, rExec, randomTask, status); } - enqueue_dirty(tasks, graph, rExec); + exec_update(tasks, graph, rExec); } } @@ -165,8 +165,8 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) { input = 1 + randGen() % 30; - pipeline_run(exec, pl.vec); - enqueue_dirty(tasks, graph, exec); + exec_request_run(exec, pl.vec); + exec_update(tasks, graph, exec); randomized_singlethreaded_execute(tasks, graph, exec, randGen, sc_totalTaskCount, [&functions, &input, &output, &checksRun] (TaskId const task) -> TaskActions { @@ -203,7 +203,7 @@ struct Pipelines } // namespace test_b -// Test that features a looping 'normal' pipeline and an 'optional' pipeline that has a 50% chance of running +// Test that features a 'normal' pipeline and an 'optional' pipeline that has a 50% chance of running TEST(Tasks, BasicSingleThreadedOptional) { using namespace test_b; @@ -266,6 +266,7 @@ TEST(Tasks, BasicSingleThreadedOptional) .sync_with({pl.optional(Read)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { + ++ rState.checks; EXPECT_TRUE(rState.normalDone); EXPECT_EQ(rState.expectOptionalDone, rState.optionalDone); return {}; @@ -275,7 +276,6 @@ TEST(Tasks, BasicSingleThreadedOptional) .run_on ({pl.normal(Clear)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { - ++ rState.checks; rState.normalDone = false; rState.expectOptionalDone = false; rState.optionalDone = false; @@ -308,8 +308,8 @@ TEST(Tasks, BasicSingleThreadedOptional) for (int i = 0; i < sc_repetitions; ++i) { - pipeline_run(exec, pl.normal); - enqueue_dirty(tasks, graph, exec); + exec_request_run(exec, pl.normal); + exec_update(tasks, graph, exec); randomized_singlethreaded_execute( tasks, graph, exec, randGen, 10, @@ -331,11 +331,12 @@ namespace test_c struct TestState { - std::vector in; - std::vector out; + std::vector inputQueue; + std::vector outputQueue; + int intermediate { 0 }; - int processA{0}; - int processB{0}; + int checks { 0 }; + int outSumExpected { 0 }; }; enum class Stages { Schedule, Process, Done, Clear }; @@ -344,13 +345,13 @@ struct Pipelines { osp::PipelineDef main; osp::PipelineDef loop; - osp::PipelineDef processA; - osp::PipelineDef processB; + osp::PipelineDef stepA; + osp::PipelineDef stepB; }; } // namespace test_c -// +// Looping pipelines with 2 child pipelines that run a 2-step process TEST(Tasks, BasicSingleThreadedLoop) { using namespace test_c; @@ -360,7 +361,7 @@ TEST(Tasks, BasicSingleThreadedLoop) using Builder_t = BasicTraits_t::Builder; using TaskFuncVec_t = BasicTraits_t::FuncVec_t; - constexpr int sc_repetitions = 128; + constexpr int sc_repetitions = 42; std::mt19937 randGen(69); Tasks tasks; @@ -370,24 +371,64 @@ TEST(Tasks, BasicSingleThreadedLoop) auto const pl = builder.create_pipelines(); - builder.pipeline(pl.loop) .parent(pl.main).loops(true); - builder.pipeline(pl.processA).parent(pl.loop); - builder.pipeline(pl.processB).parent(pl.loop); + builder.pipeline(pl.loop) .parent(pl.main).loops(true); + builder.pipeline(pl.stepA).parent(pl.loop); + builder.pipeline(pl.stepB).parent(pl.loop); + // Determine if we should loop or not builder.task() - .run_on ({pl.main(Schedule)}) + .run_on ({pl.loop(Schedule)}) + .sync_with({pl.main(Process), pl.stepA(Schedule), pl.stepB(Schedule)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { + if (rState.inputQueue.empty()) + { + return TaskAction::Cancel; + } + return { }; }); + // Consume one item from input queue and writes to intermediate value builder.task() - .run_on ({pl.loop(Schedule)}) + .run_on ({pl.stepA(Process)}) + .sync_with({pl.main(Process), pl.loop(Process)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { + rState.intermediate = rState.inputQueue.back() * 2; + rState.inputQueue.pop_back(); return { }; }); + // Read intermediate value and write to output queue + builder.task() + .run_on ({pl.stepB(Process)}) + .sync_with({pl.main(Process), pl.stepA(Done), pl.loop(Process)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions + { + rState.outputQueue.push_back(rState.intermediate + 5); + return { }; + }); + + // Verify output queue is correct + builder.task() + .run_on ({pl.main(Done)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions + { + ++ rState.checks; + int const sum = std::reduce(rState.outputQueue.begin(), rState.outputQueue.end()); + EXPECT_TRUE(rState.outSumExpected == sum); + return { }; + }); + + // Clear output queue after use + builder.task() + .run_on ({pl.main(Clear)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions + { + rState.outputQueue.clear(); + return { }; + }); TaskGraph const graph = make_exec_graph(tasks, {&edges}); @@ -398,10 +439,22 @@ TEST(Tasks, BasicSingleThreadedLoop) TestState world; + world.inputQueue.reserve(64); + world.outputQueue.reserve(64); + for (int i = 0; i < sc_repetitions; ++i) { - pipeline_run(exec, pl.main); - enqueue_dirty(tasks, graph, exec); + int outSumExpected = 0; + world.inputQueue.resize(randGen() % 64); + for (int &rNum : world.inputQueue) + { + rNum = randGen() % 64; + outSumExpected += rNum * 2 + 5; + } + world.outSumExpected = outSumExpected; + + exec_request_run(exec, pl.main); + exec_update(tasks, graph, exec); randomized_singlethreaded_execute( tasks, graph, exec, randGen, 999999, @@ -410,6 +463,141 @@ TEST(Tasks, BasicSingleThreadedLoop) return functions[task](world, randGen); }); } + + ASSERT_EQ(world.checks, sc_repetitions); +} + +//----------------------------------------------------------------------------- + +namespace test_d +{ + +struct TestState +{ + int countIn { 0 }; + + int countOut { 0 }; + int countOutExpected { 0 }; + int outerLoops { 0 }; + bool innerLoopRan { false }; + + int checks { 0 }; +}; + +enum class Stages { Schedule, Process, Done, Clear }; + +struct Pipelines +{ + osp::PipelineDef loopOuter; + osp::PipelineDef loopInner; + osp::PipelineDef aux; +}; + +} // namespace test_d + +// Looping 'outer' pipeline with a nested looping 'inner' pipeline +TEST(Tasks, BasicSingleThreadedNestedLoop) +{ + using namespace test_d; + using enum Stages; + + using BasicTraits_t = BasicBuilderTraits; + using Builder_t = BasicTraits_t::Builder; + using TaskFuncVec_t = BasicTraits_t::FuncVec_t; + + constexpr int sc_repetitions = 42; + std::mt19937 randGen(69); + + Tasks tasks; + TaskEdges edges; + TaskFuncVec_t functions; + Builder_t builder{tasks, edges, functions}; + + auto const pl = builder.create_pipelines(); + + builder.pipeline(pl.loopOuter).loops(true); + builder.pipeline(pl.loopInner).loops(true).parent(pl.loopOuter); + //builder.pipeline(pl.aux).parent(pl.loopOuter); + + builder.task() + .run_on ({pl.loopInner(Schedule)}) + .sync_with({pl.loopOuter(Process)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions + { + if (rState.countIn == 0) + { + return TaskAction::Cancel; + } + + rState.innerLoopRan = true; + return { }; + }); + + builder.task() + .run_on ({pl.loopInner(Process)}) + .sync_with({pl.loopOuter(Process)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions + { + -- rState.countIn; + ++ rState.countOut; + return { }; + }); + + builder.task() + .run_on ({pl.loopOuter(Done)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions + { + if (rState.innerLoopRan) + { + ++ rState.checks; + EXPECT_EQ(rState.countOut, rState.countOutExpected); + } + return { }; + }); + + builder.task() + .run_on ({pl.loopOuter(Clear)}) + .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions + { + rState.countOut = 0; + rState.innerLoopRan = false; + return { }; + }); + + + TaskGraph const graph = make_exec_graph(tasks, {&edges}); + + // Execute + + ExecContext exec; + exec_resize(tasks, graph, exec); + + TestState world; + + exec_request_run(exec, pl.loopOuter); + + int checksExpected = 0; + + for (int i = 0; i < sc_repetitions; ++i) + { + int const count = randGen() % 10; + + checksExpected += (count != 0); + + world.countIn = count; + world.countOutExpected = count; + + exec_update(tasks, graph, exec); + + randomized_singlethreaded_execute( + tasks, graph, exec, randGen, 50, + [&functions, &world, &randGen] (TaskId const task) -> TaskActions + { + return functions[task](world, randGen); + }); + } + + ASSERT_EQ(world.checks, checksExpected); } //----------------------------------------------------------------------------- @@ -532,11 +720,11 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Enqueue initial tasks // This roughly indicates "Time has changed" and "Render requested" - pipeline_run(exec, pl.time); - pipeline_run(exec, pl.forces); - pipeline_run(exec, pl.positions); - pipeline_run(exec, pl.render); - enqueue_dirty(tasks, graph, exec); + exec_request_run(exec, pl.time); + exec_request_run(exec, pl.forces); + exec_request_run(exec, pl.positions); + exec_request_run(exec, pl.render); + exec_update(tasks, graph, exec); randomized_singlethreaded_execute( tasks, graph, exec, randGen, 5, From 67892e6c418960e34a5869eddd78516871d4e785 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 6 Aug 2023 03:46:32 -0700 Subject: [PATCH 28/35] Implement Pipeline "Wait for (external) signal" --- src/osp/tasks/builder.h | 10 ++- src/osp/tasks/execute.cpp | 176 +++++++++++++++++++++++--------------- src/osp/tasks/execute.h | 54 ++++++++---- src/osp/tasks/tasks.cpp | 2 +- src/osp/tasks/tasks.h | 29 ++----- test/tasks/main.cpp | 37 +++----- 6 files changed, 177 insertions(+), 131 deletions(-) diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 21d8520c..4565e0bd 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -47,7 +47,7 @@ struct TaskBuilderBase using TaskRef_t = typename TRAITS_T::TaskRef_t; template - using PipelineRef_t = typename TRAITS_T::template PipelineRef; + using PipelineRef_t = typename TRAITS_T::template PipelineRef_t; constexpr TaskBuilderBase(Tasks &rTasks, TaskEdges &rEdges) noexcept : m_rTasks{rTasks} @@ -196,7 +196,13 @@ struct PipelineRefBase PipelineRef_t& loops(bool const loop) { - m_rBuilder.m_rTasks.m_pipelineControl[m_pipelineId].loops = loop; + m_rBuilder.m_rTasks.m_pipelineControl[m_pipelineId].isLoopScope = loop; + return static_cast(*this); + } + + PipelineRef_t& wait_for_signal(ENUM_T stage) + { + m_rBuilder.m_rTasks.m_pipelineControl[m_pipelineId].waitStage = StageId(stage); return static_cast(*this); } diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index b303416c..c2b4fbc8 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -34,7 +34,7 @@ static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) noexcept; static void pipeline_run_root(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) noexcept; -static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, bool useAdvanceNext, PipelineId const pipeline, PipelineTreePos_t treePos, uint32_t descendents, bool isLoopScope, bool insideLoopScope); +static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, bool rerunLoop, PipelineId pipeline, PipelineTreePos_t treePos, uint32_t descendents, bool isLoopScope, bool insideLoopScope); static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) noexcept; @@ -42,9 +42,9 @@ static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, Ex static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline) noexcept; -static constexpr bool pipeline_can_advance(Tasks const& tasks, TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept; +static constexpr bool pipeline_can_advance(ExecPipeline &rExecPl) noexcept; -static inline void pipeline_try_advance(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) noexcept; +static void pipeline_try_advance(ExecContext &rExec, ExecPipeline &rExecPl, PipelineId pipeline) noexcept; static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) noexcept; @@ -54,7 +54,7 @@ struct ArgsForIsPipelineInLoop PipelineId insideLoop; }; -static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext const& exec, ArgsForIsPipelineInLoop const args) noexcept; +static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext const& exec, ArgsForIsPipelineInLoop args) noexcept; struct ArgsForSubtreeForEach { @@ -139,7 +139,7 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe -- rExecPl.tasksQueuedRun; - pipeline_try_advance(tasks, graph, rExec, rExecPl, pipeline); + pipeline_try_advance(rExec, rExecPl, pipeline); if (actions & TaskAction::Cancel) { @@ -165,7 +165,7 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe { -- rReqExecPl.ownStageReqTasksLeft; - pipeline_try_advance(tasks, graph, rExec, rReqExecPl, reqPl); + pipeline_try_advance(rExec, rReqExecPl, reqPl); } } else @@ -191,13 +191,20 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe { -- rReqExecPl.tasksReqOwnStageLeft; - pipeline_try_advance(tasks, graph, rExec, rReqExecPl, req.reqPipeline); + pipeline_try_advance(rExec, rReqExecPl, req.reqPipeline); } // else: This pipeline is enclosed in a loop relative to dependency. This task may run // more times. } } +void exec_signal(ExecContext &rExec, PipelineId pipeline) noexcept +{ + ExecPipeline &rExecPl = rExec.plData[pipeline]; + rExecPl.waitSignaled = true; + pipeline_try_advance(rExec, rExecPl, pipeline); +} + //----------------------------------------------------------------------------- // Major steps @@ -280,7 +287,6 @@ static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext static void loop_scope_done(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline, PipelineTreePos_t const treePos) { - if ( ! rExecPl.canceled ) { // Loop more @@ -356,11 +362,11 @@ static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, E { ExecPipeline &rExecPl = rExec.plData[pipeline]; - LGRN_ASSERT(pipeline_can_advance(tasks, graph, rExec, rExecPl)); + LGRN_ASSERT(pipeline_can_advance(rExecPl)); // * rExecPl.ownStageReqTasksLeft == 0; // * rExecPl.tasksReqOwnStageLeft == 0; - int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); + auto const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); LGRN_ASSERTM(stageCount != 0, "Pipelines with 0 stages shouldn't be running"); bool const justStarting = rExecPl.stage == lgrn::id_null(); @@ -369,6 +375,11 @@ static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, E rExecPl.tasksQueueDone = false; + if (rExecPl.stage == rExecPl.waitStage) + { + rExecPl.waitSignaled = false; + } + if (nextStage != StageId(stageCount)) { exec_log(rExec, ExecContext::StageChange{pipeline, rExecPl.stage, nextStage}); @@ -408,7 +419,7 @@ static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, Ex auto const revTaskReqStageView = ArrayView(fanout_view(graph.anystgToFirstRevTaskreqstg, graph.revTaskreqstgToTask, anystg)); // Number of tasks that require this stage. This is decremented when required tasks finish - rExecPl.tasksReqOwnStageLeft = revTaskReqStageView.size(); + rExecPl.tasksReqOwnStageLeft = int(revTaskReqStageView.size()); for (TaskId const task : revTaskReqStageView) { @@ -444,7 +455,7 @@ static void pipeline_advance_reqs(Tasks const& tasks, TaskGraph const& graph, Ex auto const stgreqtaskView = ArrayView(fanout_view(graph.anystgToFirstStgreqtask, graph.stgreqtaskData, anystg)); - rExecPl.ownStageReqTasksLeft = stgreqtaskView.size(); + rExecPl.ownStageReqTasksLeft = int(stgreqtaskView.size()); // Decrement ownStageReqTasksLeft, as some of these tasks might already be complete for (StageRequiresTask const& stgreqtask : stgreqtaskView) @@ -517,7 +528,7 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe // Evaluate Task-requires-Stages // Some requirements may already be satisfied auto const taskreqstageView = ArrayView(fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)); - int reqStagesLeft = taskreqstageView.size(); + auto reqStagesLeft = int(taskreqstageView.size()); for (TaskRequiresStage const& req : taskreqstageView) { @@ -557,7 +568,7 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe rExecPl.tasksQueueDone = true; - if (noTasksRun && pipeline_can_advance(tasks, graph, rExec, rExecPl)) + if (noTasksRun && pipeline_can_advance(rExecPl)) { // No tasks to run. RunTasks are responsible for setting this pipeline dirty once they're // all done. If there is none, then this pipeline may get stuck if nothing sets it dirty, @@ -571,18 +582,63 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe // Pipeline utility + static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecContext& rExec, ExecPipeline& rExecPl, PipelineId pipeline) noexcept { PipelineTreePos_t const treePos = graph.pipelineToPltree[pipeline]; + auto const cancel_stage_ahead = [&tasks, &graph, &rExec, &rExecPl, pipeline] (AnyStageId const anystg) + { + for (TaskId task : fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)) + { + // Stages depend on this RunTask (reverse Stage-requires-Task) + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) + { + PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); + ExecPipeline &rReqExecPl = rExec.plData[reqPl]; + + if (rReqExecPl.stage == reqStg) + { + // True if 'reqPl' sees that 'pipeline' is inside a loop, and may run more times + bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = reqPl, .insideLoop = pipeline}); + + if ( ! loopsRelative ) + { + LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); + -- rReqExecPl.ownStageReqTasksLeft; + pipeline_try_advance(rExec, rReqExecPl, reqPl); + } + } + } + + // RunTask depends on stages (Task-requires-Stage) + for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) + { + ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; + + if (rReqExecPl.stage == req.reqStage) + { + bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = req.reqPipeline, .insideLoop = pipeline}); + + if ( ! loopsRelative ) + { + LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); + -- rReqExecPl.tasksReqOwnStageLeft; + pipeline_try_advance(rExec, rReqExecPl, req.reqPipeline); + } + } + } + } + }; + subtree_for_each( {.root = treePos, .includeRoot = true}, graph, rExec, - [&tasks, &graph, &rExec, &rExecPl, pipeline] + [&cancel_stage_ahead, &tasks, &graph, &rExec, &rExecPl, pipeline] (PipelineTreePos_t const cancelPos, PipelineId const cancelPl, uint32_t const descendants) { ExecPipeline &rCancelExecPl = rExec.plData[cancelPl]; - if ( ! rCancelExecPl.canceled ) { rCancelExecPl.canceled = true; @@ -590,50 +646,14 @@ static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecCont auto const stageCount = int(fanout_size(graph.pipelineToFirstAnystg, cancelPl)); auto const stagesAhead = int(rCancelExecPl.stage) + 1; + // TODO: As of now, only stages ahead that have not yet run yet are being canceled. + // This is a problem for loops, where stages behind that have already run still + // have dependencies, since they may run again. Eventually deal with these. + auto anystgInt = uint32_t(anystg_from(graph, cancelPl, StageId(stagesAhead))); for (auto stgInt = stagesAhead; stgInt < stageCount; ++stgInt, ++anystgInt) { - for (TaskId task : fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, AnyStageId(anystgInt))) - { - // Stages depend on this RunTask (reverse Stage-requires-Task) - for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) - { - PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; - StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); - ExecPipeline &rReqExecPl = rExec.plData[reqPl]; - - if (rReqExecPl.stage == reqStg) - { - // True if 'reqPl' sees that 'pipeline' is inside a loop, and may run more times - bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = reqPl, .insideLoop = pipeline}); - - if ( ! loopsRelative ) - { - LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); - -- rReqExecPl.ownStageReqTasksLeft; - pipeline_try_advance(tasks, graph, rExec, rReqExecPl, reqPl); - } - } - } - - // RunTask depends on stages (Task-requires-Stage) - for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) - { - ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; - - if (rReqExecPl.stage == req.reqStage) - { - bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = req.reqPipeline, .insideLoop = pipeline}); - - if ( ! loopsRelative ) - { - LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); - -- rReqExecPl.tasksReqOwnStageLeft; - pipeline_try_advance(tasks, graph, rExec, rReqExecPl, req.reqPipeline); - } - } - } - } + cancel_stage_ahead(AnyStageId(anystgInt)); } } @@ -641,9 +661,9 @@ static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecCont }); } -static inline void pipeline_try_advance(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) noexcept +static void pipeline_try_advance(ExecContext &rExec, ExecPipeline &rExecPl, PipelineId const pipeline) noexcept { - if (pipeline_can_advance(tasks, graph, rExec, rExecPl)) + if (pipeline_can_advance(rExecPl)) { rExec.plAdvance.set(std::size_t(pipeline)); rExec.hasPlAdvanceOrLoop = true; @@ -654,11 +674,30 @@ static inline void pipeline_try_advance(Tasks const& tasks, TaskGraph const& gra // Read-only checks -static constexpr bool pipeline_can_advance(Tasks const& tasks, TaskGraph const& graph, ExecContext const &rExec, ExecPipeline &rExecPl) noexcept +static constexpr bool pipeline_can_advance(ExecPipeline &rExecPl) noexcept { - return rExecPl.ownStageReqTasksLeft == 0 // Tasks required by stage are done - && rExecPl.tasksReqOwnStageLeft == 0 // Not required by any tasks - && (rExecPl.tasksQueuedBlocked+rExecPl.tasksQueuedRun) == 0; // Tasks done + // Pipeline can advance if... + return + // Tasks required by stage are done + rExecPl.ownStageReqTasksLeft == 0 + + // Not required by any tasks + && rExecPl.tasksReqOwnStageLeft == 0 + + // Tasks done + && ( rExecPl.tasksQueuedBlocked + rExecPl.tasksQueuedRun ) == 0 + + // Trigger checks out. only if... + && ( + // Wait is disabled + ( rExecPl.waitStage == lgrn::id_null() ) + + // Or not on specified wait stage + || ( rExecPl.waitStage != rExecPl.stage ) + + // Or wait is externally signaled to go + || rExecPl.waitSignaled + ); } @@ -740,7 +779,7 @@ static bool is_pipeline_in_loop(Tasks const& tasks, TaskGraph const& graph, Exec // Minor utility -void exec_resize(Tasks const& tasks, ExecContext &rOut) +void exec_conform(Tasks const& tasks, ExecContext &rOut) { std::size_t const maxTasks = tasks.m_taskIds.capacity(); std::size_t const maxPipeline = tasks.m_pipelineIds.capacity(); @@ -751,11 +790,12 @@ void exec_resize(Tasks const& tasks, ExecContext &rOut) bitvector_resize(rOut.plAdvance, maxPipeline); bitvector_resize(rOut.plAdvanceNext, maxPipeline); bitvector_resize(rOut.plRequestRun, maxPipeline); -} -void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut) -{ - exec_resize(tasks, rOut); + for (PipelineInt const pipelineInt : tasks.m_pipelineIds.bitview().zeros()) + { + auto const pipeline = PipelineId(pipelineInt); + rOut.plData[pipeline].waitStage = tasks.m_pipelineControl[pipeline].waitStage; + } } static void exec_log(ExecContext &rExec, ExecContext::LogMsg_t msg) noexcept diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 18991d1e..60209de0 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -38,23 +38,46 @@ namespace osp { +/** + * @brief Per-Pipeline state needed for execution + */ struct ExecPipeline { - int tasksQueuedRun {0}; - int tasksQueuedBlocked {0}; - int tasksReqOwnStageLeft{0}; - int ownStageReqTasksLeft{0}; + /// Number of Tasks with satisfied dependencies that are ready to run + int tasksQueuedRun { 0 }; + + ///< Number of Tasks waiting for their TaskReqStage dependencies to be satisfied + int tasksQueuedBlocked { 0 }; + + /** + * @brief Number of remaining TaskReqStage dependencies. + * + * Tasks from other running Pipelines require this Pipeline's current stage to be selected + * in order to run. This Pipeline has to wait for these tasks to complete before proceeding to + * the next stage. + */ + int tasksReqOwnStageLeft { 0 }; - int loopChildrenLeft {0}; + /** + * @brief Number of remaining StageReqTask dependencies. + * + * This Pipeline's current stage require Tasks from other Pipelines to complete in order to + * proceed to the next stage. + */ + int ownStageReqTasksLeft { 0 }; - StageId stage { lgrn::id_null() }; + int loopChildrenLeft { 0 }; - bool tasksQueueDone { false }; - bool loop { false }; - bool running { false }; - bool canceled { false }; - bool triggered { false }; + StageId stage { lgrn::id_null() }; + + StageId waitStage { lgrn::id_null() }; + bool waitSignaled { false }; + + bool tasksQueueDone { false }; + bool loop { false }; + bool running { false }; + bool canceled { false }; }; struct BlockedTask @@ -71,7 +94,7 @@ struct LoopRequestRun /** - * @brief The ExecLog struct + * @brief Fast plain-old-data log for ExecContext state changes */ struct ExecLog { @@ -192,10 +215,7 @@ struct ExecContext : public ExecLog }; // struct ExecContext - -void exec_resize(Tasks const& tasks, TaskGraph const& graph, ExecContext &rOut); - -void exec_resize(Tasks const& tasks, ExecContext &rOut); +void exec_conform(Tasks const& tasks, ExecContext &rOut); inline void exec_request_run(ExecContext &rExec, PipelineId pipeline) noexcept { @@ -203,7 +223,7 @@ inline void exec_request_run(ExecContext &rExec, PipelineId pipeline) noexcept rExec.hasRequestRun = true; } -void pipeline_cancel_loop(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec, PipelineId pipeline); +void exec_signal(ExecContext &rExec, PipelineId pipeline) noexcept; void exec_update(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) noexcept; diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index d8452e62..d6ad658b 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -317,7 +317,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView for (TaskCounts const& taskCount : taskCounts) { if ( taskCount.requiredByStages != 0 - || taskCount.requiredByStages != 0 ) + || taskCount.requiresStages != 0 ) { return false; } diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index a5b6dc57..9001fc45 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -67,18 +67,18 @@ enum class SemaphoreId : SemaphoreInt { }; struct PipelineInfo { using stage_type_family_t = entt::family; - using stage_type_t = stage_type_family_t::value_type; + using stage_type_t = stage_type_family_t::value_type; static inline KeyedVec> sm_stageNames; - std::string_view name; - std::string_view category; - stage_type_t stageType; + std::string_view name; + std::string_view category; + stage_type_t stageType; }; struct PipelineControl { - StageId trigger { lgrn::id_null() }; + StageId waitStage { lgrn::id_null() }; bool isLoopScope { false }; }; @@ -122,7 +122,7 @@ struct TaskEdges { std::vector m_syncWith; - std::vector m_semaphoreEdges; + //std::vector m_semaphoreEdges; }; using PipelineTreePos_t = uint32_t; @@ -199,8 +199,8 @@ struct TaskGraph KeyedVec pipelineToLoopScope; // not yet used - lgrn::IntArrayMultiMap taskAcquire; /// Tasks acquire (n) Semaphores - lgrn::IntArrayMultiMap semaAcquiredBy; /// Semaphores are acquired by (n) Tasks + //lgrn::IntArrayMultiMap taskAcquire; /// Tasks acquire (n) Semaphores + //lgrn::IntArrayMultiMap semaAcquiredBy; /// Semaphores are acquired by (n) Tasks }; @@ -284,17 +284,6 @@ inline StageId stage_from(TaskGraph const& graph, AnyStageId const stg) noexcept return stage_from(graph, graph.anystgToPipeline[stg], stg); } -constexpr StageId stage_next(StageId const in, int stageCount) noexcept -{ - int const next = int(in) + 1; - return StageId( (next == stageCount) ? 0 : next ); -} - -constexpr StageId stage_prev(StageId const in, int stageCount) noexcept -{ - return StageId( (int(in)==0) ? (stageCount-1) : (int(in)-1) ); -} - template struct PipelineDef { @@ -316,7 +305,7 @@ struct PipelineDef PipelineInfo::stage_type_t m_type { PipelineInfo::stage_type_family_t::value }; - PipelineId m_value { PipelineId(69) }; + PipelineId m_value { lgrn::id_null() }; }; using PipelineDefBlank_t = PipelineDef; diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index bf5b4dba..8d77ad90 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -154,7 +154,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) // Step 3: Run ExecContext exec; - exec_resize(tasks, graph, exec); + exec_conform(tasks, exec); int checksRun = 0; int input = 0; @@ -163,7 +163,7 @@ TEST(Tasks, BasicSingleThreadedParallelTasks) // Repeat with randomness to test many possible execution orders for (int i = 0; i < sc_repetitions; ++i) { - input = 1 + randGen() % 30; + input = 1 + int(randGen() % 30); exec_request_run(exec, pl.vec); exec_update(tasks, graph, exec); @@ -302,7 +302,7 @@ TEST(Tasks, BasicSingleThreadedOptional) // Execute ExecContext exec; - exec_resize(tasks, graph, exec); + exec_conform(tasks, exec); TestState world; @@ -435,7 +435,7 @@ TEST(Tasks, BasicSingleThreadedLoop) // Execute ExecContext exec; - exec_resize(tasks, graph, exec); + exec_conform(tasks, exec); TestState world; @@ -448,7 +448,7 @@ TEST(Tasks, BasicSingleThreadedLoop) world.inputQueue.resize(randGen() % 64); for (int &rNum : world.inputQueue) { - rNum = randGen() % 64; + rNum = int(randGen() % 64); outSumExpected += rNum * 2 + 5; } world.outSumExpected = outSumExpected; @@ -479,7 +479,6 @@ struct TestState int countOut { 0 }; int countOutExpected { 0 }; int outerLoops { 0 }; - bool innerLoopRan { false }; int checks { 0 }; }; @@ -515,9 +514,8 @@ TEST(Tasks, BasicSingleThreadedNestedLoop) auto const pl = builder.create_pipelines(); - builder.pipeline(pl.loopOuter).loops(true); + builder.pipeline(pl.loopOuter).loops(true).wait_for_signal(Schedule); builder.pipeline(pl.loopInner).loops(true).parent(pl.loopOuter); - //builder.pipeline(pl.aux).parent(pl.loopOuter); builder.task() .run_on ({pl.loopInner(Schedule)}) @@ -529,7 +527,6 @@ TEST(Tasks, BasicSingleThreadedNestedLoop) return TaskAction::Cancel; } - rState.innerLoopRan = true; return { }; }); @@ -547,11 +544,8 @@ TEST(Tasks, BasicSingleThreadedNestedLoop) .run_on ({pl.loopOuter(Done)}) .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { - if (rState.innerLoopRan) - { - ++ rState.checks; - EXPECT_EQ(rState.countOut, rState.countOutExpected); - } + ++ rState.checks; + EXPECT_EQ(rState.countOut, rState.countOutExpected); return { }; }); @@ -560,7 +554,6 @@ TEST(Tasks, BasicSingleThreadedNestedLoop) .func( [] (TestState& rState, std::mt19937 &rRand) -> TaskActions { rState.countOut = 0; - rState.innerLoopRan = false; return { }; }); @@ -570,25 +563,23 @@ TEST(Tasks, BasicSingleThreadedNestedLoop) // Execute ExecContext exec; - exec_resize(tasks, graph, exec); + exec_conform(tasks, exec); TestState world; exec_request_run(exec, pl.loopOuter); - int checksExpected = 0; - for (int i = 0; i < sc_repetitions; ++i) { - int const count = randGen() % 10; - - checksExpected += (count != 0); + auto const count = int(randGen() % 10); world.countIn = count; world.countOutExpected = count; exec_update(tasks, graph, exec); + exec_signal(exec, pl.loopOuter); + randomized_singlethreaded_execute( tasks, graph, exec, randGen, 50, [&functions, &world, &randGen] (TaskId const task) -> TaskActions @@ -597,7 +588,7 @@ TEST(Tasks, BasicSingleThreadedNestedLoop) }); } - ASSERT_EQ(world.checks, checksExpected); + ASSERT_EQ(world.checks, sc_repetitions); } //----------------------------------------------------------------------------- @@ -707,7 +698,7 @@ TEST(Tasks, BasicSingleThreadedGameWorld) // Execute ExecContext exec; - exec_resize(tasks, graph, exec); + exec_conform(tasks, exec); World world; From 0b3adc05580bcf784d8e69552f8807de7d8d3814 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Tue, 15 Aug 2023 00:40:37 -0700 Subject: [PATCH 29/35] Progress on fixing everything after pipeline nuke --- src/osp/tasks/builder.h | 22 ++++- src/osp/tasks/top_execute.cpp | 67 +++++++------ src/osp/tasks/top_session.cpp | 49 +--------- src/osp/tasks/top_session.h | 27 +----- src/osp/tasks/top_utils.h | 47 ++++++---- src/osp/tasks/top_worker.h | 2 +- .../activescenes/identifiers.h | 61 +++++++----- .../activescenes/scenarios.cpp | 94 +++++++++---------- src/test_application/activescenes/scenarios.h | 12 ++- .../activescenes/scene_common.cpp | 74 ++++++++------- .../activescenes/scene_common.h | 4 +- .../activescenes/scene_misc.cpp | 15 +-- .../activescenes/scene_misc.h | 2 +- .../activescenes/scene_newton.h | 1 - .../activescenes/scene_physics.cpp | 34 ++----- .../activescenes/scene_renderer.cpp | 67 ++++++------- .../activescenes/scene_renderer.h | 6 +- src/test_application/execution.cpp | 0 src/test_application/execution.h | 62 ++++++++++++ src/test_application/main.cpp | 18 ++-- src/test_application/testapp.cpp | 30 +++--- src/test_application/testapp.h | 20 +++- 22 files changed, 377 insertions(+), 337 deletions(-) create mode 100644 src/test_application/execution.cpp create mode 100644 src/test_application/execution.h diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 4565e0bd..ee6f0336 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -81,14 +81,16 @@ struct TaskBuilderBase } template - [[nodiscard]] TGT_STRUCT_T create_pipelines() + TGT_STRUCT_T create_pipelines(osp::ArrayView const pipelinesOut) { static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineDefBlank_t) == 0); constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineDefBlank_t); - std::array pipelines; + LGRN_ASSERTMV(count == pipelinesOut.size() , + "The number of members in TGT_STRUCT_T must match the number of output pipelines", + count, pipelinesOut.size()); - m_rTasks.m_pipelineIds.create(pipelines.begin(), pipelines.end()); + m_rTasks.m_pipelineIds.create(pipelinesOut.begin(), pipelinesOut.end()); std::size_t const capacity = m_rTasks.m_pipelineIds.capacity(); @@ -105,13 +107,23 @@ struct TaskBuilderBase for (std::size_t i = 0; i < count; ++i) { - PipelineId const pl = pipelines[i]; + PipelineId const pl = pipelinesOut[i]; *reinterpret_cast(pOutBytes + sizeof(PipelineDefBlank_t)*i + offsetof(PipelineDefBlank_t, m_value)) = pl; } - return out; } + template + [[nodiscard]] TGT_STRUCT_T create_pipelines() + { + static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineDefBlank_t) == 0); + constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineDefBlank_t); + + std::array pipelines; + + return create_pipelines(osp::ArrayView{pipelines.data(), count}); + } + template std::array create_pipelines() { diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index 5b28a884..b84d3481 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -74,10 +75,10 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec : entt::any{}); } - bool const shouldRun = (rTopTask.m_func != nullptr) && conditions_satisfied(tasks, graph, rExec, task); + bool const shouldRun = (rTopTask.m_func != nullptr); // Task actually runs here - TriggerOut_t const status = shouldRun ? rTopTask.m_func(worker, topDataRefs) : 0; + TaskActions const status = shouldRun ? rTopTask.m_func(worker, topDataRefs) : TaskActions{}; complete_task(tasks, graph, rExec, task, status); top_write_log(std::cout, tasks, rTaskData, graph, rExec); @@ -90,7 +91,7 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec std::abort(); } - enqueue_dirty(tasks, graph, rExec); + exec_update(tasks, graph, rExec); top_write_log(std::cout, tasks, rTaskData, graph, rExec); rExec.logMsg.clear(); } @@ -105,7 +106,7 @@ static void write_task_requirements(std::ostream &rStream, Tasks const& tasks, T PipelineInfo const& info = tasks.m_pipelineInfo[req.reqPipeline]; auto const stageNames = ArrayView{PipelineInfo::sm_stageNames[info.stageType]}; - if (reqPlData.currentStage != req.reqStage) + if (reqPlData.stage != req.reqStage) { rStream << "* Requires PL" << std::setw(3) << PipelineInt(req.reqPipeline) << " stage " << stageNames[std::size_t(req.reqStage)] << "\n"; } @@ -134,12 +135,9 @@ void top_write_pipeline_states(std::ostream &rStream, Tasks const& tasks, TopTas for (int stage = 0; stage < stageCount; ++stage) { - bool const trg = plExec.triggered.test(stage); - bool const sel = int(plExec.currentStage) == stage; + bool const sel = int(plExec.stage) == stage; rStream << (sel ? '[' : ' ') - << (trg ? '*' : ' ') << stageNames[stage] - << (trg ? '*' : ' ') << (sel ? ']' : ' '); charsUsed += 4 + stageNames[stage].size(); @@ -177,24 +175,43 @@ void top_write_log(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t c auto const visitMsg = [&rStream, &tasks, &taskData, &graph, &stage_name] (auto&& msg) { using MSG_T = std::decay_t; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { - rStream << "EnqueueStart\n"; + rStream << "UpdateStart\n"; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - rStream << "EnqueueCycle\n"; + rStream << "UpdateCycle\n"; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - rStream << "EnqueueEnd\n"; + rStream << "UpdateEnd\n"; + } + else if constexpr (std::is_same_v) + { + rStream << " PipelineRun PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; + } + else if constexpr (std::is_same_v) + { + rStream << " PipelineFinish PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; + } + else if constexpr (std::is_same_v) + { + rStream << " PipelineCancel PL" << std::setw(3) << PipelineInt(msg.pipeline) << "(" + << stage_name(msg.pipeline, msg.stage) << ")\n"; + } + else if constexpr (std::is_same_v) + { + rStream << " PipelineLoop PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; + } + else if constexpr (std::is_same_v) + { + rStream << " PipelineLoopFinish PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; } else if constexpr (std::is_same_v) { - rStream << " StageChange PL" << std::setw(3) << PipelineInt(msg.pipeline) << "(" - << stage_name(msg.pipeline, msg.stageOld) - << " -> " - << stage_name(msg.pipeline, msg.stageNew) << ")\n"; + rStream << " StageChange PL" << std::setw(3) << PipelineInt(msg.pipeline) + << "(" << stage_name(msg.pipeline, msg.stageOld) << " -> " << stage_name(msg.pipeline, msg.stageNew) << ")\n"; } else if constexpr (std::is_same_v) { @@ -205,7 +222,9 @@ void top_write_log(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t c } else if constexpr (std::is_same_v) { - rStream << " * Requires PL" << std::setw(3) << PipelineInt(msg.pipeline) << "(" << stage_name(msg.pipeline, msg.stage) << ")\n"; + rStream << " * " << (msg.satisfied ? "[DONE]" : "[wait]") << "Require PL" + << std::setw(3) << PipelineInt(msg.pipeline) + << "(" << stage_name(msg.pipeline, msg.stage) << ")\n"; } else if constexpr (std::is_same_v) { @@ -215,15 +234,9 @@ void top_write_log(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t c { rStream << "Complete TASK" << TaskInt(msg.task) << " - " << taskData[msg.task].m_debugName << "\n"; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - rStream << "* Trigger PL" << std::setw(3) << PipelineInt(msg.pipeline) - << "(" << stage_name(msg.pipeline, msg.stage) << ")\n"; - } - else if constexpr (std::is_same_v) - { - rStream << "ExternalTrigger PL" << std::setw(3) << PipelineInt(msg.pipeline) - << "(" << stage_name(msg.pipeline, msg.stage) << ")\n"; + rStream << "ExternalRunRequest PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; } }; diff --git a/src/osp/tasks/top_session.cpp b/src/osp/tasks/top_session.cpp index 1d624ae3..7a98e976 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/osp/tasks/top_session.cpp @@ -23,8 +23,6 @@ * SOFTWARE. */ #include "top_session.h" -#include "top_execute.h" -#include "execute.h" #include "tasks.h" #include @@ -36,50 +34,5 @@ namespace osp { -void top_close_session( - Tasks & rTasks, - TaskGraph const& graph, - TopTaskDataVec_t& rTaskData, - ArrayView topData, - ExecContext& rExec, - ArrayView sessions) -{ - // Run cleanup pipelines - for (Session &rSession : sessions) - { - if (rSession.m_cleanup.pipeline != lgrn::id_null()) - { - exec_trigger(rExec, {rSession.m_cleanup.pipeline, rSession.m_cleanup.stage}); - } - } - enqueue_dirty(rTasks, graph, rExec); - top_run_blocking(rTasks, graph, rTaskData, topData, rExec); - - // Clear each session's TopData - for (Session &rSession : sessions) - { - for (TopDataId const id : std::exchange(rSession.m_data, {})) - { - if (id != lgrn::id_null()) - { - topData[std::size_t(id)].reset(); - } - } - } - - // Clear each session's tasks - for (Session &rSession : sessions) - { - for (TaskId const task : rSession.m_tasks) - { - rTasks.m_taskIds.remove(task); - - TopTask &rCurrTaskData = rTaskData[task]; - rCurrTaskData.m_debugName.clear(); - rCurrTaskData.m_dataUsed.clear(); - rCurrTaskData.m_func = nullptr; - } - } -} -} // namespace testapp +} // namespace osp diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index 6a1f661e..93a78af6 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -24,10 +24,7 @@ */ #pragma once -#include "builder.h" -#include "execute.h" #include "tasks.h" -#include "top_tasks.h" #include "top_utils.h" #include @@ -42,7 +39,7 @@ #define OSP_AUX_DCDI_C(session, topData, count, ...) \ session.m_data.resize(count); \ - top_reserve(topData, 0, session.m_data.begin(), session.m_data.end()); \ + osp::top_reserve(topData, 0, session.m_data.begin(), session.m_data.end()); \ auto const [__VA_ARGS__] = osp::unpack(session.m_data) #define OSP_AUX_DCDI_B(x) x #define OSP_AUX_DCDI_A(...) OSP_AUX_DCDI_B(OSP_AUX_DCDI_C(__VA_ARGS__)) @@ -94,22 +91,7 @@ struct Session m_pipelines.resize(count); - rBuilder.m_rTasks.m_pipelineIds.create(m_pipelines.begin(), m_pipelines.end()); - rBuilder.m_rTasks.m_pipelineInfo.resize(rBuilder.m_rTasks.m_pipelineIds.capacity()); - - TGT_STRUCT_T out; - auto const members = Corrade::Containers::staticArrayView(reinterpret_cast(&out)); - - for (std::size_t i = 0; i < count; ++i) - { - PipelineId const pl = m_pipelines[i]; - members[i].m_value = pl; - - rBuilder.m_rTasks.m_pipelineInfo[pl].stageType = members[i].m_type; - rBuilder.m_rTasks.m_pipelineInfo[pl].name = members[i].m_name; - } - - return out; + return rBuilder.template create_pipelines(ArrayView(m_pipelines.data(), count)); } template @@ -152,10 +134,5 @@ struct SessionGroup TaskEdges m_edges; }; -/** - * @brief Close sessions, delete all their associated TopData, Tasks, and Targets. - */ -void top_close_session(Tasks& rTasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, ArrayView sessions); - } // namespace osp diff --git a/src/osp/tasks/top_utils.h b/src/osp/tasks/top_utils.h index bd3a46e4..2367af8d 100644 --- a/src/osp/tasks/top_utils.h +++ b/src/osp/tasks/top_utils.h @@ -130,14 +130,14 @@ struct wrap_args_trait } template - static TriggerOut_t wrapped_task([[maybe_unused]] WorkerContext ctx, ArrayView topData) noexcept + static TaskActions wrapped_task([[maybe_unused]] WorkerContext ctx, ArrayView topData) noexcept { if constexpr (std::is_void_v) { cast_args(topData, ctx, std::make_index_sequence{}); - return gc_triggerAll; + return {}; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { return cast_args(topData, ctx, std::make_index_sequence{}); } @@ -181,15 +181,26 @@ constexpr TopTaskFunc_t wrap_args(FUNC_T funcArg) //----------------------------------------------------------------------------- -struct TopTaskRef; +struct TopTaskBuilder; +struct TopTaskTaskRef; + +struct TopTaskBuilderTraits +{ + using Builder_t = TopTaskBuilder; + using TaskRef_t = TopTaskTaskRef; + + template + using PipelineRef_t = PipelineRefBase; +}; + /** * @brief Convenient interface for building TopTasks */ -struct TopTaskBuilder : public TaskBuilderBase +struct TopTaskBuilder : public TaskBuilderBase { TopTaskBuilder(Tasks& rTasks, TaskEdges& rEdges, TopTaskDataVec_t& rData) - : TaskBuilderBase( {rTasks, rEdges} ) + : TaskBuilderBase( {rTasks, rEdges} ) , m_rData{rData} { } TopTaskBuilder(TopTaskBuilder const& copy) = delete; @@ -200,29 +211,29 @@ struct TopTaskBuilder : public TaskBuilderBase TopTaskDataVec_t & m_rData; }; -struct TopTaskRef : public TaskRefBase +struct TopTaskTaskRef : public TaskRefBase { - inline TopTaskRef& name(std::string_view debugName); - inline TopTaskRef& args(std::initializer_list dataUsed); + inline TopTaskTaskRef& name(std::string_view debugName); + inline TopTaskTaskRef& args(std::initializer_list dataUsed); template - TopTaskRef& func(FUNC_T&& funcArg); - inline TopTaskRef& func_raw(TopTaskFunc_t func); + TopTaskTaskRef& func(FUNC_T&& funcArg); + inline TopTaskTaskRef& func_raw(TopTaskFunc_t func); - inline TopTaskRef& important_deps_count(int value); + inline TopTaskTaskRef& important_deps_count(int value); template - TopTaskRef& push_to(CONTAINER_T& rContainer); + TopTaskTaskRef& push_to(CONTAINER_T& rContainer); }; -TopTaskRef& TopTaskRef::name(std::string_view debugName) +TopTaskTaskRef& TopTaskTaskRef::name(std::string_view debugName) { m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_debugName = debugName; return *this; } -TopTaskRef& TopTaskRef::args(std::initializer_list dataUsed) +TopTaskTaskRef& TopTaskTaskRef::args(std::initializer_list dataUsed) { m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_dataUsed = dataUsed; @@ -230,14 +241,14 @@ TopTaskRef& TopTaskRef::args(std::initializer_list dataUsed) } template -TopTaskRef& TopTaskRef::func(FUNC_T&& funcArg) +TopTaskTaskRef& TopTaskTaskRef::func(FUNC_T&& funcArg) { m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_func = wrap_args(funcArg); return *this; } -TopTaskRef& TopTaskRef::func_raw(TopTaskFunc_t func) +TopTaskTaskRef& TopTaskTaskRef::func_raw(TopTaskFunc_t func) { m_rBuilder.m_rData.resize(m_rBuilder.m_rTasks.m_taskIds.capacity()); m_rBuilder.m_rData[m_taskId].m_func = func; @@ -252,7 +263,7 @@ TopTaskRef& TopTaskRef::func_raw(TopTaskFunc_t func) //} template -TopTaskRef& TopTaskRef::push_to(CONTAINER_T& rContainer) +TopTaskTaskRef& TopTaskTaskRef::push_to(CONTAINER_T& rContainer) { rContainer.push_back(m_taskId); return *this; diff --git a/src/osp/tasks/top_worker.h b/src/osp/tasks/top_worker.h index c45455b8..8b647f8b 100644 --- a/src/osp/tasks/top_worker.h +++ b/src/osp/tasks/top_worker.h @@ -48,6 +48,6 @@ struct WorkerContext //DependOnDirty_t m_dependOnDirty; }; -using TopTaskFunc_t = TriggerOut_t(*)(WorkerContext, ArrayView) noexcept; +using TopTaskFunc_t = TaskActions(*)(WorkerContext, ArrayView) noexcept; } // namespace osp diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index d99dad62..6a6bb6bf 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -32,20 +32,29 @@ namespace testapp { -enum class EStgFlag : uint8_t +enum class EStgOptn : uint8_t { - Wait_, - Write + Schedule, + Run, + Done }; -OSP_DECLARE_STAGE_NAMES(EStgFlag, "Wait", "Write"); +OSP_DECLARE_STAGE_NAMES(EStgOptn, "Schedule", "Run", "Done"); enum class EStgEvnt : uint8_t +{ + Run_, + Done_ +}; +OSP_DECLARE_STAGE_NAMES(EStgEvnt, "Run", "Done"); + + +enum class EStgGate : uint8_t { Wait, - Run + Open }; -OSP_DECLARE_STAGE_NAMES(EStgEvnt, "Wait", "Run"); +OSP_DECLARE_STAGE_NAMES(EStgGate, "Wait", "Open"); /** @@ -55,10 +64,11 @@ enum class EStgIntr : uint8_t { Resize, Modify_, - Use_, + Schedule_, + UseOrRun, Clear }; -OSP_DECLARE_STAGE_NAMES(EStgIntr, "Resize", "Modify", "Use", "Clear"); +OSP_DECLARE_STAGE_NAMES(EStgIntr, "Resize", "Modify", "Schedule", "Use", "Clear"); /** @@ -78,35 +88,40 @@ enum class EStgCont : uint8_t Modify, ///< Modify existing elements - Use + Ready ///< Container is ready to use }; OSP_DECLARE_STAGE_NAMES(EStgCont, "Delete", "New", "Modify", "Use"); -enum class EStgRender +enum class EStgFBO { Bind, Draw, Unbind }; -OSP_DECLARE_STAGE_NAMES(EStgRender, "Bind", "Draw", "Unbind"); +OSP_DECLARE_STAGE_NAMES(EStgFBO, "Bind", "Draw", "Unbind"); using osp::PipelineDef; //----------------------------------------------------------------------------- +#define TESTAPP_DATA_APPLICATION 1, \ + idResources +struct PlApplication +{ + PipelineDef mainLoop {"mainLoop - ..."}; +}; + +//----------------------------------------------------------------------------- + #define TESTAPP_DATA_SCENE 1, \ idDeltaTimeIn struct PlScene { PipelineDef cleanup {"cleanup - Scene cleanup before destruction"}; - PipelineDef resyncAll {"resyncAll - Resynchronize with renderer"}; - - PipelineDef updTime {"time - External Delta Time In"}; - PipelineDef updActive {"updActive - Updates on ActiveEnt and components"}; - PipelineDef updDraw {"updDraw - Updates on DrawEnt and components"}; + PipelineDef update {"update - main loop "}; }; #define TESTAPP_DATA_COMMON_SCENE 6, \ @@ -114,14 +129,14 @@ struct PlScene struct PlCommonScene { PipelineDef activeEnt {"activeEnt"}; - PipelineDef activeEntResized {"activeEntResized"}; + PipelineDef activeEntResized {"activeEntResized"}; PipelineDef activeEntDelete {"activeEntDelete"}; PipelineDef transform {"transform"}; PipelineDef hierarchy {"hierarchy"}; PipelineDef drawEnt {"drawEnt"}; - PipelineDef drawEntResized {"drawEntResized"}; + PipelineDef drawEntResized {"drawEntResized"}; PipelineDef drawEntDelete {"drawEntDelete"}; PipelineDef mesh {"mesh"}; @@ -130,8 +145,8 @@ struct PlCommonScene PipelineDef entTextureDirty {"entTextureDirty"}; PipelineDef entMeshDirty {"entMeshDirty"}; - PipelineDef meshResDirty {"meshResDirty"}; - PipelineDef textureResDirty {"textureResDirty"}; + PipelineDef meshResDirty {"meshResDirty"}; + PipelineDef textureResDirty {"textureResDirty"}; PipelineDef material {"material"}; PipelineDef materialDirty {"materialDirty"}; @@ -282,8 +297,7 @@ struct PlNewton idUserInput struct PlWindowApp { - PipelineDef inputs {"inputs - User inputs in"}; - PipelineDef display {"display - Display new frame"}; + PipelineDef inputs {"render"}; }; @@ -307,7 +321,8 @@ struct PlMagnum idScnRender, idGroupFwd, idCamera struct PlSceneRenderer { - PipelineDef fboRender {"fboRender"}; + PipelineDef render {"render"}; + PipelineDef fbo {"fboRender"}; PipelineDef scnRender {"scnRender"}; PipelineDef group {"group"}; diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 1efe5de5..a4308636 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -62,46 +62,41 @@ static constexpr auto sc_matPhong = active::MaterialId(2); static constexpr int sc_materialCount = 4; -static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& scnRenderer, std::vector run = {}) +static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& scnRenderer, std::vector run = {}) { - OSP_DECLARE_GET_DATA_IDS(scnRenderer, TESTAPP_DATA_COMMON_RENDERER); - OSP_DECLARE_GET_DATA_IDS(rTestApp.m_windowApp, TESTAPP_DATA_WINDOW_APP); - OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); - - auto &rActiveApp = top_get(rTestApp.m_topData, idActiveApp); - auto &rCamera = top_get(rTestApp.m_topData, idCamera); - rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); - - auto tgScn = scene .get_pipelines(); - auto tgWin = rTestApp.m_windowApp .get_pipelines(); - - // Resynchronize scene with new renderer - exec_resize(rTestApp.m_tasks, rTestApp.m_exec); - exec_trigger(rTestApp.m_exec, tgScn.resyncAll(EStgEvnt::Run)); - exec_trigger(rTestApp.m_exec, tgScn.updDraw(EStgEvnt::Run)); - exec_trigger(rTestApp.m_exec, tgScn.updActive(EStgEvnt::Run)); - - run.insert(run.end(), { - tgScn.updTime (EStgEvnt::Run), - tgScn.updActive (EStgEvnt::Run), - tgScn.updDraw (EStgEvnt::Run), - tgWin.inputs (EStgEvnt::Run), - tgWin.display (EStgEvnt::Run)}); - - // run gets copied but who cares lol - rActiveApp.set_on_draw( [&rTestApp, run = std::move(run), resync = tgScn.resyncAll] - (ActiveApplication& rApp, float delta) - { - // Magnum Application's main loop is here +// OSP_DECLARE_GET_DATA_IDS(scnRenderer, TESTAPP_DATA_COMMON_RENDERER); +// OSP_DECLARE_GET_DATA_IDS(rTestApp.m_windowApp, TESTAPP_DATA_WINDOW_APP); +// OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); - for (auto const [pipeline, stage] : run) - { - exec_trigger(rTestApp.m_exec, {pipeline, stage}); - } +// auto &rActiveApp = top_get(rTestApp.m_topData, idActiveApp); +// auto &rCamera = top_get(rTestApp.m_topData, idCamera); +// rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); + +// auto tgScn = scene .get_pipelines(); +// auto tgWin = rTestApp.m_windowApp .get_pipelines(); - enqueue_dirty(rTestApp.m_tasks, *rTestApp.m_graph, rTestApp.m_exec); +// // Resynchronize scene with new renderer +// exec_conform(rTestApp.m_tasks, rTestApp.m_exec); +// exec_request_run(rTestApp.m_exec, tgScn.resyncAll); +// exec_request_run(rTestApp.m_exec, tgScn.updDraw); +// exec_request_run(rTestApp.m_exec, tgScn.updActive); - top_run_blocking(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec); +// run.insert(run.end(), { tgScn.updTime, tgScn.updActive, tgScn.updDraw, tgWin.inputs, tgWin.display}); + +// // run gets copied but who cares lol +// rActiveApp.set_on_draw( [&rTestApp, run = std::move(run), resync = tgScn.resyncAll] +// (ActiveApplication& rApp, float delta) +// { +// // Magnum Application's main loop is here + +// for (PipelineId const pipeline : run) +// { +// exec_request_run(rTestApp.m_exec, pipeline); +// } + +// exec_update(rTestApp.m_tasks, *rTestApp.m_graph, rTestApp.m_exec); + +// top_run_blocking(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec); // Enqueued tasks that don't run indicate a deadlock // if ( ! std::all_of(std::begin(rExec.m_taskQueuedCounts), @@ -112,7 +107,7 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c // debug_top_print_deadlock( rTasks, rTaskData, rExec); // std::abort(); // } - }); +// }); } template @@ -128,11 +123,12 @@ static ScenarioMap_t make_scenarios() PipelineInfo::sm_stageNames.resize(32); - register_stage_enum(); + register_stage_enum(); register_stage_enum(); + register_stage_enum(); register_stage_enum(); register_stage_enum(); - register_stage_enum(); + register_stage_enum(); auto const add_scenario = [&scenarioMap] (std::string_view name, std::string_view desc, SceneSetupFunc_t run) { @@ -145,7 +141,10 @@ static ScenarioMap_t make_scenarios() SessionGroup &rOut = rTestApp.m_scene; rOut.m_sessions.resize(1); TopDataId const idSceneData = rOut.m_sessions[0].acquire_data<1>(rTestApp.m_topData)[0]; - auto &rResources = top_get(rTestApp.m_topData, rTestApp.m_idResources); + + OSP_DECLARE_CREATE_DATA_IDS(rTestApp.m_application, rTestApp.m_topData, TESTAPP_DATA_APPLICATION); + + auto &rResources = top_get(rTestApp.m_topData, idResources); // enginetest::setup_scene returns an entt::any containing one big // struct containing all the scene data. @@ -173,7 +172,7 @@ static ScenarioMap_t make_scenarios() using namespace testapp::scenes; auto const defaultPkg = rTestApp.m_defaultPkg; - auto const idResources = rTestApp.m_idResources; + auto const application = rTestApp.m_application; auto & rTopData = rTestApp.m_topData; TopTaskBuilder builder{rTestApp.m_tasks, rTestApp.m_scene.m_edges, rTestApp.m_taskData}; @@ -184,7 +183,7 @@ static ScenarioMap_t make_scenarios() // Compose together lots of Sessions scene = setup_scene (builder, rTopData); - commonScene = setup_common_scene (builder, rTopData, scene, idResources, defaultPkg); + commonScene = setup_common_scene (builder, rTopData, scene, application, defaultPkg); physics = setup_physics (builder, rTopData, scene, commonScene); shapeSpawn = setup_shape_spawn (builder, rTopData, scene, commonScene, physics, sc_matVisualizer); //droppers = setup_droppers (builder, rTopData, commonScene, shapeSpawn); @@ -196,23 +195,20 @@ static ScenarioMap_t make_scenarios() //shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, commonScene, physics, shapeSpawn, newton, nwtGravSet); create_materials(rTopData, commonScene, sc_materialCount); - add_floor(rTopData, commonScene, shapeSpawn, sc_matVisualizer, idResources, defaultPkg); + add_floor(rTopData, application, commonScene, shapeSpawn, sc_matVisualizer, defaultPkg); auto const tgScn = scene .get_pipelines(); auto const tgCS = commonScene .get_pipelines(); auto const tgShSp = shapeSpawn .get_pipelines(); - exec_resize(rTestApp.m_tasks, rTestApp.m_exec); - exec_trigger(rTestApp.m_exec, tgShSp.spawnRequest(Use_)); - exec_trigger(rTestApp.m_exec, tgScn.updDraw(EStgEvnt::Run)); - exec_trigger(rTestApp.m_exec, tgScn.updActive(EStgEvnt::Run)); + //exec_conform(rTestApp.m_tasks, rTestApp.m_exec); return [] (TestApp& rTestApp) { + auto const application = rTestApp.m_application; auto const windowApp = rTestApp.m_windowApp; auto const magnum = rTestApp.m_magnum; auto const defaultPkg = rTestApp.m_defaultPkg; - auto const idResources = rTestApp.m_idResources; auto & rTopData = rTestApp.m_topData; TopTaskBuilder builder{rTestApp.m_tasks, rTestApp.m_renderer.m_edges, rTestApp.m_taskData}; @@ -225,7 +221,7 @@ static ScenarioMap_t make_scenarios() scnRender, cameraCtrl, cameraFree, shVisual, camThrow ] = resize_then_unpack<5>(rTestApp.m_renderer.m_sessions); - scnRender = setup_scene_renderer (builder, rTopData, windowApp, magnum, scene, commonScene, idResources); + scnRender = setup_scene_renderer (builder, rTopData, application, windowApp, magnum, scene, commonScene); cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, scnRender); cameraFree = setup_camera_free (builder, rTopData, windowApp, scene, cameraCtrl); shVisual = setup_shader_visualizer (builder, rTopData, magnum, scene, commonScene, scnRender, sc_matVisualizer); diff --git a/src/test_application/activescenes/scenarios.h b/src/test_application/activescenes/scenarios.h index 9b7c3db8..656fad49 100644 --- a/src/test_application/activescenes/scenarios.h +++ b/src/test_application/activescenes/scenarios.h @@ -39,13 +39,21 @@ namespace testapp namespace scenes { + using enum EStgOptn; using enum EStgCont; + using enum EStgGate; using enum EStgIntr; using enum EStgEvnt; - using enum EStgFlag; - using enum EStgRender; + using enum EStgFBO; } +struct MainLoopControl +{ + bool doUpdate; + bool doRenderResync; + bool doRender; +}; + struct ScenarioOption { std::string_view m_desc; diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index 28bd474c..b99b928a 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -58,9 +58,11 @@ Session setup_common_scene( TopTaskBuilder& rBuilder, ArrayView const topData, Session const& scene, - TopDataId const idResources, + Session const& application, PkgId const pkg) { + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); + auto const tgScn = scene.get_pipelines(); auto &rResources = top_get< Resources > (topData, idResources); @@ -75,24 +77,26 @@ Session setup_common_scene( auto &rDrawingRes = top_emplace< ACtxDrawingRes > (topData, idDrawingRes); auto &rNMesh = top_emplace< NamedMeshes > (topData, idNMesh); - rBuilder.task() - .name ("Set materials, meshes, and textures dirty") - .run_on ({tgScn.resyncAll(Run)}) - //.sync_with ({tgCS.texture(Modify), tgCS.mesh(Modify)}) - .triggers ({tgCS.entTextureDirty(Use_), tgCS.entMeshDirty(Use_), tgCS.materialDirty(Use_), tgCS.meshResDirty(Run), tgCS.textureResDirty(Run), tgCS.transform(Use), tgCS.drawEnt(New), tgCS.drawEnt(Use), tgCS.drawEntResized(Write), }) - .push_to (out.m_tasks) - .args ({ idDrawing }) - .func([] (ACtxDrawing& rDrawing) noexcept - { - SysRender::set_dirty_all(rDrawing); - return gc_triggerAll; - }); + rBuilder.pipeline(tgCS.activeEnt) .parent(tgScn.update); + rBuilder.pipeline(tgCS.activeEntResized) .parent(tgScn.update); + rBuilder.pipeline(tgCS.activeEntDelete) .parent(tgScn.update); + rBuilder.pipeline(tgCS.transform) .parent(tgScn.update); + rBuilder.pipeline(tgCS.hierarchy) .parent(tgScn.update); + rBuilder.pipeline(tgCS.drawEnt) .parent(tgScn.update); + rBuilder.pipeline(tgCS.drawEntResized) .parent(tgScn.update); + rBuilder.pipeline(tgCS.drawEntDelete) .parent(tgScn.update); + rBuilder.pipeline(tgCS.mesh) .parent(tgScn.update); + rBuilder.pipeline(tgCS.texture) .parent(tgScn.update); + rBuilder.pipeline(tgCS.entTextureDirty) .parent(tgScn.update); + rBuilder.pipeline(tgCS.entMeshDirty) .parent(tgScn.update); + rBuilder.pipeline(tgCS.meshResDirty) .parent(tgScn.update); + rBuilder.pipeline(tgCS.textureResDirty) .parent(tgScn.update); + rBuilder.pipeline(tgCS.material) .parent(tgScn.update); + rBuilder.pipeline(tgCS.materialDirty) .parent(tgScn.update); rBuilder.task() .name ("Delete ActiveEnt IDs") - .run_on ({tgScn.updActive(Run)}) - .conditions ({tgCS.activeEntDelete(Use_)}) - .sync_with ({tgCS.activeEnt(Delete)}) + .run_on ({tgCS.activeEnt(Delete)}) .push_to (out.m_tasks) .args ({ idBasic, idActiveEntDel }) .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -106,10 +110,19 @@ Session setup_common_scene( } }); + rBuilder.task() + .name ("Cancel entity delete tasks stuff if no entities were deleted") + .run_on ({tgCS.activeEntDelete(Schedule_)}) + .push_to (out.m_tasks) + .args ({ idBasic, idActiveEntDel }) + .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept + { + return rActiveEntDel.empty() ? TaskAction::Cancel : TaskActions{}; + }); + rBuilder.task() .name ("Delete basic components") - .run_on ({tgScn.updActive(Run)}) - .conditions ({tgCS.activeEntDelete(Use_)}) + .run_on ({tgCS.activeEntDelete(UseOrRun)}) .sync_with ({tgCS.transform(Delete)}) .push_to (out.m_tasks) .args ({ idBasic, idActiveEntDel }) @@ -120,10 +133,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEntity of deleted ActiveEnts") - .run_on ({tgScn.updActive(Run)}) - .conditions ({tgCS.activeEntDelete(Use_) }) + .run_on ({tgCS.activeEntDelete(UseOrRun)}) .sync_with ({tgCS.drawEntDelete(Modify_)}) - .triggers ({tgCS.drawEntDelete (Use_), tgCS.drawEntDelete(Clear)}) .push_to (out.m_tasks) .args ({ idDrawing, idActiveEntDel, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, ActiveEntVec_t const& rActiveEntDel, DrawEntVec_t& rDrawEntDel) noexcept @@ -136,14 +147,11 @@ Session setup_common_scene( rDrawEntDel.push_back(drawEnt); } } - - return rDrawEntDel.empty() ? gc_triggerNone : gc_triggerAll; }); rBuilder.task() .name ("Delete drawing components") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.drawEntDelete(Use_)}) + .run_on ({tgCS.drawEntDelete(UseOrRun)}) .sync_with ({tgCS.mesh(Delete), tgCS.texture(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) @@ -154,9 +162,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEntity IDs") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.drawEntDelete(Use_)}) - .sync_with ({tgCS.drawEnt (Delete)}) + .run_on ({tgCS.drawEntDelete(UseOrRun)}) + .sync_with ({tgCS.drawEnt(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept @@ -172,9 +179,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete DrawEnt from materials") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.drawEntDelete(Use_)}) - .sync_with ({tgCS.material (Delete)}) + .run_on ({tgCS.drawEntDelete(UseOrRun)}) + .sync_with ({tgCS.material(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept @@ -223,8 +229,8 @@ Session setup_common_scene( rBuilder.task() .name ("Clean up scene and resource owners") - .run_on ({tgScn.cleanup(Run)}) - .sync_with ({tgCS.mesh(Delete), tgCS.texture(Delete)}) + .run_on ({tgScn.cleanup(Run_)}) + //.sync_with ({tgCS.mesh(Delete), tgCS.texture(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idResources}) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources) noexcept @@ -235,7 +241,7 @@ Session setup_common_scene( rBuilder.task() .name ("Clean up NamedMeshes mesh and texture owners") - .run_on ({tgScn.cleanup(Run)}) + .run_on ({tgScn.cleanup(Run_)}) .push_to (out.m_tasks) .args ({ idDrawing, idNMesh }) .func([] (ACtxDrawing& rDrawing, NamedMeshes& rNMesh) noexcept diff --git a/src/test_application/activescenes/scene_common.h b/src/test_application/activescenes/scene_common.h index f0e2253f..ef7f9f43 100644 --- a/src/test_application/activescenes/scene_common.h +++ b/src/test_application/activescenes/scene_common.h @@ -50,7 +50,7 @@ struct NamedMeshes osp::Session setup_scene( osp::TopTaskBuilder& rBuilder, - osp::ArrayView const topData); + osp::ArrayView topData); /** * @brief Support for Time, ActiveEnts, Hierarchy, Transforms, Drawing, and more... @@ -59,7 +59,7 @@ osp::Session setup_common_scene( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, osp::Session const& scene, - osp::TopDataId idResources, + osp::Session const& application, osp::PkgId pkg); diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index 89946606..9bf4db98 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -70,12 +70,13 @@ void create_materials( void add_floor( ArrayView const topData, + Session const& application, Session const& commonScene, Session const& shapeSpawn, MaterialId const materialId, - TopDataId const idResources, PkgId const pkg) { + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); @@ -164,8 +165,8 @@ Session setup_camera_ctrl( rBuilder.task() .name ("Position Rendering Camera according to Camera Controller") - .run_on ({tgWin.inputs(Run)}) - .sync_with ({tgCmCt.camCtrl(Use), tgSR.camera(Modify)}) + .run_on ({tgSR.render(Run)}) + .sync_with ({tgCmCt.camCtrl(Ready), tgSR.camera(Modify)}) .push_to (out.m_tasks) .args ({ idCamCtrl, idCamera }) .func([] (ACtxCameraController const& rCamCtrl, Camera &rCamera) noexcept @@ -195,15 +196,12 @@ Session setup_camera_free( .name ("Move Camera controller") .run_on ({tgWin.inputs(Run)}) .sync_with ({tgCmCt.camCtrl(Modify)}) - .triggers ({tgCmCt.camCtrl(Use)}) .push_to (out.m_tasks) .args ({ idCamCtrl, idDeltaTimeIn }) .func([] (ACtxCameraController& rCamCtrl, float const deltaTimeIn) noexcept { SysCameraController::update_view(rCamCtrl, deltaTimeIn); SysCameraController::update_move(rCamCtrl, deltaTimeIn, true); - - return gc_triggerAll; }); return out; @@ -233,8 +231,7 @@ Session setup_thrower( rBuilder.task() .name ("Throw spheres when pressing space") .run_on ({tgWin.inputs(Run)}) - .sync_with ({tgCmCt.camCtrl(Use), tgShSp.spawnRequest(Modify_)}) - .triggers ({tgShSp.spawnRequest(Use_)}) + .sync_with ({tgCmCt.camCtrl(Ready), tgShSp.spawnRequest(Modify_)}) .push_to (out.m_tasks) .args ({ idCamCtrl, idSpawner, idBtnThrow }) .func([] (ACtxCameraController& rCamCtrl, ACtxShapeSpawner& rSpawner, EButtonControlIndex btnThrow) noexcept @@ -252,9 +249,7 @@ Session setup_thrower( .m_mass = 1.0f, .m_shape = EShape::Sphere }); - return gc_triggerAll; } - return gc_triggerNone; }); return out; diff --git a/src/test_application/activescenes/scene_misc.h b/src/test_application/activescenes/scene_misc.h index 07f7ca10..e8427ff8 100644 --- a/src/test_application/activescenes/scene_misc.h +++ b/src/test_application/activescenes/scene_misc.h @@ -38,10 +38,10 @@ void create_materials( void add_floor( osp::ArrayView topData, + osp::Session const& application, osp::Session const& commonScene, osp::Session const& shapeSpawn, osp::active::MaterialId material, - osp::TopDataId idResources, osp::PkgId pkg); /** diff --git a/src/test_application/activescenes/scene_newton.h b/src/test_application/activescenes/scene_newton.h index 85b18e12..55738f29 100644 --- a/src/test_application/activescenes/scene_newton.h +++ b/src/test_application/activescenes/scene_newton.h @@ -28,7 +28,6 @@ #include #include -#include #include #include #include diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index c2b47e1e..d8700781 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -68,8 +68,7 @@ Session setup_physics( rBuilder.task() .name ("Delete Physics components") - .run_on ({tgScn.updActive(Run)}) - .conditions ({tgCS.activeEntDelete(Use_)}) + .run_on ({tgCS.activeEntDelete(UseOrRun)}) .sync_with ({tgPhy.physics(Delete)}) .push_to (out.m_tasks) .args ({ idPhys, idActiveEntDel }) @@ -102,10 +101,8 @@ Session setup_shape_spawn( top_emplace< ACtxShapeSpawner > (topData, idSpawner, ACtxShapeSpawner{ .m_materialId = materialId }); rBuilder.task() .name ("Create entities for requested shapes to spawn") - .run_on ({tgScn.updActive(Run)}) - .conditions ({tgShSp.spawnRequest(Use_)}) + .run_on ({tgShSp.spawnRequest(UseOrRun)}) .sync_with ({tgCS.activeEnt(New), tgShSp.spawnedEnts(Resize)}) - .triggers ({tgCS.activeEnt(Use), tgShSp.spawnedEnts(Use_)}) .push_to (out.m_tasks) .args ({ idBasic, idSpawner }) .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept @@ -114,16 +111,12 @@ Session setup_shape_spawn( rSpawner.m_ents.resize(rSpawner.m_spawnRequest.size() * 2); rBasic.m_activeIds.create(rSpawner.m_ents.begin(), rSpawner.m_ents.end()); - - return gc_triggerAll; }); rBuilder.task() .name ("Add hierarchy and transform to spawned shapes") - .run_on ({tgScn.updActive(Run)}) - .conditions ({tgShSp.spawnRequest(Use_)}) - .sync_with ({tgShSp.spawnedEnts(Use_), tgCS.hierarchy(New), tgCS.transform(New)}) - .triggers ({tgCS.transform(Use)}) + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgCS.hierarchy(New), tgCS.transform(New)}) .push_to (out.m_tasks) .args ({ idBasic, idSpawner }) .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept @@ -142,16 +135,12 @@ Session setup_shape_spawn( SubtreeBuilder bldRoot = bldScnRoot.add_child(root, 1); bldRoot.add_child(child); } - - return gc_triggerAll; }); rBuilder.task() .name ("Add mesh and material to spawned shapes") - .run_on ({tgScn.updActive(Run)}) - .conditions ({tgShSp.spawnRequest(Use_)}) - .sync_with ({tgShSp.spawnedEnts(Use_), tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New)}) - .triggers ({tgCS.drawEntResized(Write)}) + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idSpawner, idNMesh }) .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept @@ -188,15 +177,12 @@ Session setup_shape_spawn( rDrawing.m_visible.set(std::size_t(drawEnt)); rDrawing.m_drawBasic[drawEnt].m_opaque = true; } - - return gc_triggerAll; }); rBuilder.task() .name ("Add physics to spawned shapes") - .run_on ({tgScn.updActive(Run)}) - .conditions ({tgShSp.spawnRequest(Use_)}) - .sync_with ({tgShSp.spawnedEnts(Use_), tgPhy.physics(New)}) + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgPhy.physics(New)}) .push_to (out.m_tasks) .args ({ idBasic, idSpawner, idPhys }) .func([] (ACtxBasic const& rBasic, ACtxShapeSpawner& rSpawner, ACtxPhysics& rPhys) noexcept @@ -228,8 +214,8 @@ Session setup_shape_spawn( rBuilder.task() .name ("Clear Shape Spawning vector after use") - .run_on ({tgScn.updActive(Run)}) - .conditions ({tgShSp.spawnRequest(Clear)}) + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnRequest(Clear)}) .push_to (out.m_tasks) .args ({ idSpawner }) .func([] (ACtxShapeSpawner& rSpawner) noexcept diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index ae51bc40..26763c5c 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -82,16 +82,17 @@ Session setup_magnum( TopTaskBuilder& rBuilder, ArrayView const topData, Session const& windowApp, - TopDataId const idResources, + Session const& application, ActiveApplication::Arguments args) { - OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); + OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); auto& rUserInput = top_get(topData, idUserInput); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_MAGNUM); auto const tgMgn = out.create_pipelines(rBuilder); - out.m_cleanup = tgMgn.cleanup.tpl(Run); + out.m_cleanup = tgMgn.cleanup.tpl(Run_); // Order-dependent; ActiveApplication construction starts OpenGL context, needed by RenderGL /* unused */ top_emplace(topData, idActiveApp, args, rUserInput); @@ -101,7 +102,7 @@ Session setup_magnum( rBuilder.task() .name ("Clean up Magnum renderer") - .run_on ({tgMgn.cleanup(Run)}) + .run_on ({tgMgn.cleanup(Run_)}) .push_to (out.m_tasks) .args ({ idResources, idRenderGl}) .func([] (Resources& rResources, RenderGL& rRenderGl) noexcept @@ -117,12 +118,13 @@ Session setup_magnum( Session setup_scene_renderer( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& application, Session const& windowApp, Session const& magnum, Session const& scene, - Session const& commonScene, - TopDataId const idResources) + Session const& commonScene) { + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); @@ -146,8 +148,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Resize Scene Render containers to fit drawable entities") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.drawEntResized(Write)}) + .run_on ({tgCS.drawEntResized(Run)}) .sync_with ({tgSR.entMesh(New), tgSR.scnRender(New)}) .push_to (out.m_tasks) .args ({ idDrawing, idScnRender}) @@ -161,9 +162,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Compile Resource Meshes to GL") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.meshResDirty(Run)}) - .sync_with ({tgCS.mesh(Use), tgMgn.meshGL(New)}) + .run_on ({tgCS.meshResDirty(UseOrRun)}) + .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(New)}) .push_to (out.m_tasks) .args ({ idDrawingRes, idResources, idRenderGl }) .func([] (ACtxDrawingRes const& rDrawingRes, osp::Resources& rResources, RenderGL& rRenderGl) noexcept @@ -173,9 +173,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Compile Resource Textures to GL") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.textureResDirty(Run)}) - .sync_with ({tgCS.texture(Use), tgMgn.textureGL(New)}) + .run_on ({tgCS.textureResDirty(UseOrRun)}) + .sync_with ({tgCS.texture(Ready), tgMgn.textureGL(New)}) .push_to (out.m_tasks) .args ({ idDrawingRes, idResources, idRenderGl }) .func([] (ACtxDrawingRes const& rDrawingRes, osp::Resources& rResources, RenderGL& rRenderGl) noexcept @@ -185,9 +184,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Assign GL textures to entities with scene textures") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.entTextureDirty(Use_)}) - .sync_with ({tgCS.texture(Use), tgMgn.textureGL(Use), tgMgn.entTextureGL(Modify)}) + .run_on ({tgCS.entTextureDirty(UseOrRun)}) + .sync_with ({tgCS.texture(Ready), tgMgn.textureGL(Ready), tgMgn.entTextureGL(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept @@ -197,9 +195,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Assign GL meshes to entities with scene meshes") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.entMeshDirty(Use_)}) - .sync_with ({tgCS.mesh(Use), tgMgn.meshGL(Use), tgMgn.entMeshGL(Modify)}) + .run_on ({tgCS.entMeshDirty(UseOrRun)}) + .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(Ready), tgMgn.entMeshGL(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept @@ -209,9 +206,8 @@ Session setup_scene_renderer( rBuilder.task() .name ("Bind and display off-screen FBO") - .run_on ({tgWin.display(Run)}) - .sync_with ({tgSR.fboRender(Bind)}) - .triggers ({tgSR.fboRender(Draw), tgSR.fboRender(Unbind)}) + .run_on ({tgSR.render(Run)}) + .sync_with ({tgSR.fbo(EStgFBO::Bind)}) .push_to (out.m_tasks) .args ({ idDrawing, idRenderGl, idGroupFwd, idCamera }) .func([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera) noexcept @@ -228,16 +224,12 @@ Session setup_scene_renderer( // Clear it rFbo.clear( FramebufferClear::Color | FramebufferClear::Depth | FramebufferClear::Stencil); - - return gc_triggerAll; }); rBuilder.task() .name ("Calculate draw transforms") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.transform(Use)}) - .sync_with ({tgCS.hierarchy(Use), tgCS.activeEnt(Use), tgSR.drawTransforms(Modify_), tgCS.drawEnt(Use)}) - .triggers ({tgSR.drawTransforms(Use_)}) + .run_on ({tgSR.render(Run)}) + .sync_with ({tgCS.hierarchy(Ready), tgCS.activeEnt(Ready), tgSR.drawTransforms(Modify_), tgCS.drawEnt(Ready)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idScnRender }) .func([] (ACtxBasic const& rBasic, ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept @@ -251,16 +243,14 @@ Session setup_scene_renderer( rDrawing .m_needDrawTf, rootChildren.begin(), rootChildren.end()); - - return gc_triggerAll; }); rBuilder.task() .name ("Render Entities") - .run_on ({tgSR.fboRender(Draw)}) - .sync_with ({tgSR.group(Use), tgSR.groupEnts(Use), tgSR.camera(Use), tgSR.drawTransforms(Use_), tgSR.entMesh(Use), tgSR.entTexture(Use), - tgMgn.entMeshGL(Use), tgMgn.entTextureGL(Use), - tgCS.drawEnt(Use)}) + .run_on ({tgSR.render(Run)}) + .sync_with ({tgSR.group(Ready), tgSR.groupEnts(Ready), tgSR.camera(Ready), tgSR.drawTransforms(UseOrRun), tgSR.entMesh(Ready), tgSR.entTexture(Ready), + tgMgn.entMeshGL(Ready), tgMgn.entTextureGL(Ready), + tgCS.drawEnt(Ready)}) .push_to (out.m_tasks) .args ({ idDrawing, idRenderGl, idGroupFwd, idCamera }) .func([] (ACtxDrawing const& rDrawing, RenderGL& rRenderGl, RenderGroup const& rGroupFwd, Camera const& rCamera, WorkerContext ctx) noexcept @@ -273,7 +263,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Delete entities from render groups") - .run_on ({tgCS.drawEntDelete(Use_)}) + .run_on ({tgCS.drawEntDelete(UseOrRun)}) .sync_with ({tgSR.groupEnts (Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idGroupFwd, idDrawEntDel }) @@ -327,9 +317,8 @@ Session setup_shader_visualizer( rBuilder.task() .name ("Sync MeshVisualizer shader entities") - .run_on ({tgScn.updDraw(Run)}) - .conditions ({tgCS.materialDirty(Use_)}) - .sync_with ({tgSR.groupEnts(Use), tgSR.group(Modify)}) + .run_on ({tgCS.materialDirty(UseOrRun)}) + .sync_with ({tgSR.groupEnts(Ready), tgSR.group(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idGroupFwd, idDrawShVisual}) .func([] (ACtxDrawing const& rDrawing, RenderGroup& rGroupFwd, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index dbc8f84d..02fb5700 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -42,7 +42,7 @@ osp::Session setup_magnum( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, osp::Session const& windowApp, - osp::TopDataId idResources, + osp::Session const& application, ActiveApplication::Arguments args); /** @@ -51,11 +51,11 @@ osp::Session setup_magnum( osp::Session setup_scene_renderer( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& application, osp::Session const& windowApp, osp::Session const& magnum, osp::Session const& scene, - osp::Session const& commonScene, - osp::TopDataId idResources); + osp::Session const& commonScene); /** * @brief Magnum MeshVisualizer shader and optional material for drawing ActiveEnts with it diff --git a/src/test_application/execution.cpp b/src/test_application/execution.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/test_application/execution.h b/src/test_application/execution.h new file mode 100644 index 00000000..cbff0549 --- /dev/null +++ b/src/test_application/execution.h @@ -0,0 +1,62 @@ +#include + +namespace testapp +{ + + + + + +} // namespace testapp +/** + * @brief Close sessions, delete all their associated TopData, Tasks, and Targets. + */ +void top_close_session(Tasks& rTasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, ArrayView sessions); + + + +void top_close_session( + Tasks & rTasks, + TaskGraph const& graph, + TopTaskDataVec_t& rTaskData, + ArrayView topData, + ExecContext& rExec, + ArrayView sessions) +{ + // Run cleanup pipelines + for (Session &rSession : sessions) + { + if (rSession.m_cleanup.pipeline != lgrn::id_null()) + { + exec_request_run(rExec, rSession.m_cleanup.pipeline); + } + } + exec_update(rTasks, graph, rExec); + top_run_blocking(rTasks, graph, rTaskData, topData, rExec); + + // Clear each session's TopData + for (Session &rSession : sessions) + { + for (TopDataId const id : std::exchange(rSession.m_data, {})) + { + if (id != lgrn::id_null()) + { + topData[std::size_t(id)].reset(); + } + } + } + + // Clear each session's tasks + for (Session &rSession : sessions) + { + for (TaskId const task : rSession.m_tasks) + { + rTasks.m_taskIds.remove(task); + + TopTask &rCurrTaskData = rTaskData[task]; + rCurrTaskData.m_debugName.clear(); + rCurrTaskData.m_dataUsed.clear(); + rCurrTaskData.m_func = nullptr; + } + } +} diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index 87595618..10ca24d7 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -259,7 +259,7 @@ void start_magnum_async() osp::TopTaskBuilder builder{g_testApp.m_tasks, g_testApp.m_renderer.m_edges, g_testApp.m_taskData}; g_testApp.m_windowApp = scenes::setup_window_app (builder, g_testApp.m_topData); - g_testApp.m_magnum = scenes::setup_magnum (builder, g_testApp.m_topData, g_testApp.m_windowApp, g_testApp.m_idResources, {g_argc, g_argv}); + g_testApp.m_magnum = scenes::setup_magnum (builder, g_testApp.m_topData, g_testApp.m_windowApp, g_testApp.m_application, {g_argc, g_argv}); OSP_DECLARE_GET_DATA_IDS(g_testApp.m_magnum, TESTAPP_DATA_MAGNUM); // declares idActiveApp auto &rActiveApp = osp::top_get(g_testApp.m_topData, idActiveApp); @@ -268,12 +268,12 @@ void start_magnum_async() g_testApp.m_rendererSetup(g_testApp); - g_testApp.m_graph.reset(); - g_testApp.m_graph.emplace(osp::make_exec_graph(g_testApp.m_tasks, {&g_testApp.m_renderer.m_edges, &g_testApp.m_scene.m_edges})); - osp::exec_resize(g_testApp.m_tasks, *g_testApp.m_graph, g_testApp.m_exec); + //g_testApp.m_graph.reset(); + //g_testApp.m_graph.emplace(osp::make_exec_graph(g_testApp.m_tasks, {&g_testApp.m_renderer.m_edges, &g_testApp.m_scene.m_edges})); + //osp::exec_conform(g_testApp.m_tasks, g_testApp.m_exec); - enqueue_dirty(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_exec); - top_run_blocking(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_taskData, g_testApp.m_topData, g_testApp.m_exec); + //exec_update(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_exec); + //top_run_blocking(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_taskData, g_testApp.m_topData, g_testApp.m_exec); // Starts the main loop. This function is blocking, and will only return // once the window is closed. See ActiveApplication::drawEvent @@ -305,9 +305,11 @@ void load_a_bunch_of_stuff() std::size_t const maxTags = 256; // aka: just two 64-bit integers std::size_t const maxTagsInts = maxTags / 64; - g_testApp.m_idResources = osp::top_reserve(g_testApp.m_topData); + // declares idResources + //g_testApp.m_application.create_pipelines<> + OSP_DECLARE_CREATE_DATA_IDS(g_testApp.m_application, g_testApp.m_topData, TESTAPP_DATA_APPLICATION); - auto &rResources = osp::top_emplace(g_testApp.m_topData, g_testApp.m_idResources); + auto &rResources = osp::top_emplace(g_testApp.m_topData, idResources); rResources.resize_types(osp::ResTypeIdReg_t::size()); diff --git a/src/test_application/testapp.cpp b/src/test_application/testapp.cpp index 20418614..ce39ef0e 100644 --- a/src/test_application/testapp.cpp +++ b/src/test_application/testapp.cpp @@ -24,6 +24,8 @@ */ #include "testapp.h" +#include "activescenes/identifiers.h" + #include #include @@ -34,28 +36,25 @@ namespace testapp void close_sessions(TestAppTasks &rTestApp, osp::SessionGroup &rSessions) { - rSessions.m_edges.m_runOn .clear(); - rSessions.m_edges.m_syncWith .clear(); - rSessions.m_edges.m_triggers .clear(); - rSessions.m_edges.m_semaphoreEdges .clear(); +// rSessions.m_edges.m_syncWith .clear(); - if ( rSessions.m_sessions.empty() || ! rTestApp.m_graph.has_value() ) - { - return; - } +// if ( rSessions.m_sessions.empty() || ! rTestApp.m_graph.has_value() ) +// { +// return; +// } - TestApp testapp; - testapp.m_topData.clear(); +// TestApp testapp; +// testapp.m_topData.clear(); - osp::top_close_session(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec, rSessions.m_sessions); +// osp::top_close_session(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec, rSessions.m_sessions); - rSessions.m_sessions.clear(); +// rSessions.m_sessions.clear(); } void close_session(TestAppTasks &rTestApp, osp::Session &rSession) { - osp::top_close_session(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec, osp::ArrayView(&rSession, 1)); + //osp::top_close_session(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec, osp::ArrayView(&rSession, 1)); } @@ -76,7 +75,10 @@ void clear_resource_owners(TestApp& rTestApp) { using namespace osp::restypes; - auto &rResources = osp::top_get(rTestApp.m_topData, rTestApp.m_idResources); + // declares idResources + OSP_DECLARE_CREATE_DATA_IDS(rTestApp.m_application, rTestApp.m_topData, TESTAPP_DATA_APPLICATION); + + auto &rResources = osp::top_get(rTestApp.m_topData, idResources); // Texture resources contain osp::TextureImgSource, which refererence counts // their associated image data diff --git a/src/test_application/testapp.h b/src/test_application/testapp.h index 056c4911..8971c0b5 100644 --- a/src/test_application/testapp.h +++ b/src/test_application/testapp.h @@ -44,26 +44,40 @@ using RendererSetupFunc_t = void(*)(TestApp&); using SceneSetupFunc_t = RendererSetupFunc_t(*)(TestApp&); + + struct TestAppTasks { std::vector m_topData; osp::Tasks m_tasks; osp::TopTaskDataVec_t m_taskData; - osp::ExecContext m_exec; - std::optional m_graph; + osp::TaskGraph m_graph; +}; + +class IExecutor +{ + virtual void load(TestAppTasks const* pTasks) = 0; + virtual void run(osp::PipelineId pipeline) = 0; + virtual void signal(osp::PipelineId pipeline) = 0; + virtual void wait() = 0; + virtual bool is_done() = 0; }; struct TestApp : TestAppTasks { + osp::Session m_application; + osp::SessionGroup m_scene; osp::Session m_windowApp; osp::Session m_magnum; osp::SessionGroup m_renderer; + IExecutor* m_executor{ nullptr }; + RendererSetupFunc_t m_rendererSetup { nullptr }; - osp::TopDataId m_idResources { lgrn::id_null() }; + //osp::TopDataId m_idResources { lgrn::id_null() }; osp::PkgId m_defaultPkg { lgrn::id_null() }; }; From 6a2eda02ac072d2d40692d5706c493908970785e Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 20 Aug 2023 14:36:21 -0700 Subject: [PATCH 30/35] Fix pipeline execute bug related to loops --- src/osp/tasks/execute.cpp | 30 +++++++++++++++++++++--------- src/osp/tasks/execute.h | 2 ++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index c2b4fbc8..4d795352 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -239,6 +239,8 @@ static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext rExecPl.running = true; rExecPl.loop = isLoopScope || insideLoopScope; + ++ rExec.pipelinesRunning; + if (rerunLoop) { rExec.plAdvanceNext.set(std::size_t(pipeline)); @@ -261,27 +263,31 @@ static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext PipelineTreePos_t const childPosLast = treePos + 1 + descendents; PipelineTreePos_t childPos = treePos + 1; - int childCount = 0; + int scopeChildCount = 0; while (childPos != childPosLast) { - uint32_t const childDescendents = graph.pltreeDescendantCounts[childPos]; + uint32_t const childDescendents = graph.pltreeDescendantCounts[childPos]; + PipelineId const childPl = graph.pltreeToPipeline[childPos]; + bool const childIsLoopScope = tasks.m_pipelineControl[childPl].isLoopScope; - PipelineId const childPl = graph.pltreeToPipeline[childPos]; - bool const childIsLoopScope = tasks.m_pipelineControl[childPl].isLoopScope; + int const childChildCount = pipeline_run(tasks, graph, rExec, rerunLoop, childPl, childPos, childDescendents, childIsLoopScope, isLoopScope || insideLoopScope); - pipeline_run(tasks, graph, rExec, rerunLoop, childPl, childPos, childDescendents, childIsLoopScope, isLoopScope || insideLoopScope); + scopeChildCount += childChildCount + 1; // + 1 for child itself - ++ childCount; childPos += 1 + childDescendents; } if (isLoopScope) { - rExecPl.loopChildrenLeft = childCount; - } + rExecPl.loopChildrenLeft = scopeChildCount; - return childCount; + return 0; + } + else + { + return scopeChildCount; + } } @@ -309,6 +315,9 @@ static void loop_scope_done(Tasks const& tasks, TaskGraph const& graph, ExecCont rLoopExecPl.canceled = false; rLoopExecPl.loop = false; + LGRN_ASSERT(rExec.pipelinesRunning != 0); + -- rExec.pipelinesRunning; + exec_log(rExec, ExecContext::PipelineLoopFinish{loopPipeline}); }); @@ -398,6 +407,9 @@ static void pipeline_advance_stage(Tasks const& tasks, TaskGraph const& graph, E rExecPl.running = false; rExecPl.canceled = false; + LGRN_ASSERT(rExec.pipelinesRunning != 0); + -- rExec.pipelinesRunning; + exec_log(rExec, ExecContext::PipelineFinish{pipeline}); } } diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 60209de0..703d001b 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -204,6 +204,8 @@ struct ExecContext : public ExecLog std::vector requestLoop; bool hasRequestRun {false}; + int pipelinesRunning {0}; + // TODO: Consider multithreading. something something work stealing... // * Allow multiple threads to search for and execute tasks. Atomic access // for ExecContext? Might be messy to implement. From b20b41172c33cf49b955b94ea0de46a07bb90b02 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Wed, 23 Aug 2023 01:29:23 -0700 Subject: [PATCH 31/35] Fix a lot of stuff --- src/osp/UserInputHandler.h | 3 + src/osp/tasks/builder.h | 37 ++++- src/osp/tasks/execute.cpp | 108 +++++++++++-- src/osp/tasks/execute.h | 12 +- src/osp/tasks/tasks.cpp | 15 +- src/osp/tasks/tasks.h | 18 ++- src/osp/tasks/top_execute.cpp | 136 +++++++++++----- src/osp/tasks/top_execute.h | 20 ++- src/osp/tasks/top_session.h | 5 +- src/osp/tasks/top_tasks.h | 3 - ...eApplication.cpp => MagnumApplication.cpp} | 36 ++--- ...ctiveApplication.h => MagnumApplication.h} | 59 ++++--- .../activescenes/identifiers.h | 33 ++-- .../activescenes/scenarios.cpp | 151 +++++++++++------- src/test_application/activescenes/scenarios.h | 5 +- .../activescenes/scenarios_enginetest.cpp | 88 ++++++---- .../activescenes/scenarios_enginetest.h | 10 +- .../activescenes/scene_common.cpp | 21 ++- .../activescenes/scene_common.h | 3 +- .../activescenes/scene_misc.cpp | 4 +- .../activescenes/scene_physics.cpp | 12 +- .../activescenes/scene_renderer.cpp | 75 +++++++-- .../activescenes/scene_renderer.h | 7 +- src/test_application/execution.cpp | 0 src/test_application/execution.h | 62 ------- src/test_application/executor.cpp | 76 +++++++++ .../executor.h} | 32 +++- src/test_application/main.cpp | 99 ++++++++---- src/test_application/testapp.cpp | 62 +++++-- src/test_application/testapp.h | 42 ++--- test/tasks/main.cpp | 4 +- 31 files changed, 852 insertions(+), 386 deletions(-) rename src/test_application/{ActiveApplication.cpp => MagnumApplication.cpp} (89%) rename src/test_application/{ActiveApplication.h => MagnumApplication.h} (68%) delete mode 100644 src/test_application/execution.cpp delete mode 100644 src/test_application/execution.h create mode 100644 src/test_application/executor.cpp rename src/{osp/tasks/top_session.cpp => test_application/executor.h} (63%) diff --git a/src/osp/UserInputHandler.h b/src/osp/UserInputHandler.h index 08269274..f1663d78 100644 --- a/src/osp/UserInputHandler.h +++ b/src/osp/UserInputHandler.h @@ -359,6 +359,9 @@ class ControlSubscriber : m_pInputHandler(pInputHandler) { } + ControlSubscriber(ControlSubscriber&& move) noexcept = default; + ControlSubscriber& operator=(ControlSubscriber&& move) noexcept = default; + ~ControlSubscriber(); EButtonControlIndex button_subscribe(std::string_view name); diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index ee6f0336..8b52b942 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -103,12 +103,17 @@ struct TaskBuilderBase // Set m_value members of TGT_STRUCT_T, asserted to contain only PipelineDef<...> // This is janky enough that rewriting the code below might cause it to ONLY SEGFAULT ON // RELEASE AND ISN'T CAUGHT BY ASAN WTF??? (on gcc 11) - auto *pOutBytes = reinterpret_cast(std::addressof(out)); + auto *pOutBytes = reinterpret_cast(std::addressof(out)); for (std::size_t i = 0; i < count; ++i) { PipelineId const pl = pipelinesOut[i]; - *reinterpret_cast(pOutBytes + sizeof(PipelineDefBlank_t)*i + offsetof(PipelineDefBlank_t, m_value)) = pl; + unsigned char *pDefBytes = pOutBytes + sizeof(PipelineDefBlank_t)*i; + + *reinterpret_cast(pDefBytes + offsetof(PipelineDefBlank_t, m_value)) = pl; + + m_rTasks.m_pipelineInfo[pl].stageType = *reinterpret_cast(pDefBytes + offsetof(PipelineDefBlank_t, m_type)); + m_rTasks.m_pipelineInfo[pl].name = *reinterpret_cast(pDefBytes + offsetof(PipelineDefBlank_t, m_name)); } return out; } @@ -173,6 +178,13 @@ struct TaskRefBase return static_cast(*this); } + TaskRef_t& schedules(TplPipelineStage const tpl) noexcept + { + m_rBuilder.m_rTasks.m_pipelineControl[tpl.pipeline].scheduler = m_taskId; + + return run_on(tpl); + } + TaskRef_t& sync_with(ArrayView const specs) noexcept { return add_edges(m_rBuilder.m_rEdges.m_syncWith, specs); @@ -206,6 +218,25 @@ struct PipelineRefBase return static_cast(*this); } + PipelineRef_t& parent_with_schedule(PipelineId const parent) + { + m_rBuilder.m_rTasks.m_pipelineParents[m_pipelineId] = parent; + + constexpr ENUM_T const scheduleStage = stage_schedule(ENUM_T{0}); + static_assert(scheduleStage != lgrn::id_null(), "Pipeline type has no schedule stage"); + + TaskId const scheduler = m_rBuilder.m_rTasks.m_pipelineControl[parent].scheduler; + LGRN_ASSERTM(scheduler != lgrn::id_null(), "Parent Pipeline has no scheduler task"); + + m_rBuilder.m_rEdges.m_syncWith.push_back({ + .task = scheduler, + .pipeline = m_pipelineId, + .stage = StageId(scheduleStage) + }); + + return static_cast(*this); + } + PipelineRef_t& loops(bool const loop) { m_rBuilder.m_rTasks.m_pipelineControl[m_pipelineId].isLoopScope = loop; @@ -265,7 +296,7 @@ struct BasicBuilderTraits TaskRef& func(FUNC_T && in); }; -}; +}; // struct BasicBuilderTraits template typename BasicBuilderTraits::TaskRef& BasicBuilderTraits::TaskRef::func(FUNC_T && in) diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 4d795352..47ff9389 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -77,7 +77,8 @@ void exec_update(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExec) { for (ExecPipeline const& rExecPl : rExec.plData) { - LGRN_ASSERTM( ! rExecPl.running, "Running new pipelines while already running is not yet supported ROFL!"); + LGRN_ASSERTM( ! rExecPl.running, "Running new pipelines while already running is not yet supported ROFL! " + "Make sure all pipelines are finished running."); } for (PipelineInt const plInt : rExec.plRequestRun.ones()) @@ -163,8 +164,8 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe if ( ( ! loopsRelative ) || rExecPl.canceled ) { + LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); -- rReqExecPl.ownStageReqTasksLeft; - pipeline_try_advance(rExec, rReqExecPl, reqPl); } } @@ -189,8 +190,8 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe if ( ( ! loopsRelative ) || rExecPl.canceled ) { + LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); -- rReqExecPl.tasksReqOwnStageLeft; - pipeline_try_advance(rExec, rReqExecPl, req.reqPipeline); } // else: This pipeline is enclosed in a loop relative to dependency. This task may run @@ -201,8 +202,13 @@ void complete_task(Tasks const& tasks, TaskGraph const& graph, ExecContext &rExe void exec_signal(ExecContext &rExec, PipelineId pipeline) noexcept { ExecPipeline &rExecPl = rExec.plData[pipeline]; - rExecPl.waitSignaled = true; - pipeline_try_advance(rExec, rExecPl, pipeline); + exec_log(rExec, ExecLog::ExternalSignal{pipeline, ! rExecPl.waitSignaled}); + + if ( ! rExecPl.waitSignaled ) + { + rExecPl.waitSignaled = true; + pipeline_try_advance(rExec, rExecPl, pipeline); + } } //----------------------------------------------------------------------------- @@ -236,21 +242,22 @@ static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext if (stageCount != 0) { - rExecPl.running = true; - rExecPl.loop = isLoopScope || insideLoopScope; + if ( ! rExecPl.running ) + { + rExecPl.running = true; + rExecPl.loop = isLoopScope || insideLoopScope; - ++ rExec.pipelinesRunning; + ++ rExec.pipelinesRunning; + + rExec.plAdvance.set(std::size_t(pipeline)); + exec_log(rExec, ExecContext::PipelineRun{pipeline}); + } if (rerunLoop) { rExec.plAdvanceNext.set(std::size_t(pipeline)); exec_log(rExec, ExecContext::PipelineLoop{pipeline}); } - else - { - rExec.plAdvance.set(std::size_t(pipeline)); - exec_log(rExec, ExecContext::PipelineRun{pipeline}); - } rExec.hasPlAdvanceOrLoop = true; } @@ -311,6 +318,8 @@ static void loop_scope_done(Tasks const& tasks, TaskGraph const& graph, ExecCont { ExecPipeline &rLoopExecPl = rExec.plData[loopPipeline]; + LGRN_ASSERT(rLoopExecPl.running == true); + rLoopExecPl.running = false; rLoopExecPl.canceled = false; rLoopExecPl.loop = false; @@ -546,6 +555,8 @@ static void pipeline_advance_run(Tasks const& tasks, TaskGraph const& graph, Exe { ExecPipeline const &rReqPlData = rExec.plData[req.reqPipeline]; + LGRN_ASSERTMV(rReqPlData.running, "Required pipelines must be running", TaskInt(task), PipelineInt(req.reqPipeline)); + if (rReqPlData.stage == req.reqStage) { reqStagesLeft --; @@ -644,9 +655,64 @@ static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecCont } }; + auto const cancel_stage_blocked = [&tasks, &graph, &rExec, &rExecPl, pipeline] (AnyStageId const anystg, [[maybe_unused]] int blockedExpected) + { + for (TaskId task : fanout_view(graph.anystgToFirstRuntask, graph.runtaskToTask, anystg)) + { + if ( ! rExec.tasksQueuedBlocked.contains(task) ) + { + continue; + } + + -- blockedExpected; + rExec.tasksQueuedBlocked.erase(task); + + // Stages depend on this RunTask (reverse Stage-requires-Task) + for (AnyStageId const& reqTaskAnystg : fanout_view(graph.taskToFirstRevStgreqtask, graph.revStgreqtaskToStage, task)) + { + PipelineId const reqPl = graph.anystgToPipeline[reqTaskAnystg]; + StageId const reqStg = stage_from(graph, reqPl, reqTaskAnystg); + ExecPipeline &rReqExecPl = rExec.plData[reqPl]; + + if (rReqExecPl.stage == reqStg) + { + // True if 'reqPl' sees that 'pipeline' is inside a loop, and may run more times + bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = reqPl, .insideLoop = pipeline}); + + if ( ! loopsRelative ) + { + LGRN_ASSERT(rReqExecPl.ownStageReqTasksLeft != 0); + -- rReqExecPl.ownStageReqTasksLeft; + pipeline_try_advance(rExec, rReqExecPl, reqPl); + } + } + } + + // RunTask depends on stages (Task-requires-Stage) + for (TaskRequiresStage const& req : fanout_view(graph.taskToFirstTaskreqstg, graph.taskreqstgData, task)) + { + ExecPipeline &rReqExecPl = rExec.plData[req.reqPipeline]; + + if (rReqExecPl.stage == req.reqStage) + { + bool const loopsRelative = rExecPl.loop && is_pipeline_in_loop(tasks, graph, rExec, {.viewedFrom = req.reqPipeline, .insideLoop = pipeline}); + + if ( ! loopsRelative ) + { + LGRN_ASSERT(rReqExecPl.tasksReqOwnStageLeft != 0); + -- rReqExecPl.tasksReqOwnStageLeft; + pipeline_try_advance(rExec, rReqExecPl, req.reqPipeline); + } + } + } + } + + LGRN_ASSERT(blockedExpected == 0); + }; + subtree_for_each( {.root = treePos, .includeRoot = true}, graph, rExec, - [&cancel_stage_ahead, &tasks, &graph, &rExec, &rExecPl, pipeline] + [&cancel_stage_ahead, &cancel_stage_blocked, &tasks, &graph, &rExec, &rExecPl, pipeline] (PipelineTreePos_t const cancelPos, PipelineId const cancelPl, uint32_t const descendants) { ExecPipeline &rCancelExecPl = rExec.plData[cancelPl]; @@ -655,6 +721,12 @@ static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecCont { rCancelExecPl.canceled = true; + if (rCancelExecPl.tasksQueuedBlocked != 0) + { + cancel_stage_blocked(anystg_from(graph, cancelPl, StageId(rCancelExecPl.stage)), rCancelExecPl.tasksQueuedBlocked); + rCancelExecPl.tasksQueuedBlocked = 0; + } + auto const stageCount = int(fanout_size(graph.pipelineToFirstAnystg, cancelPl)); auto const stagesAhead = int(rCancelExecPl.stage) + 1; @@ -667,9 +739,9 @@ static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecCont { cancel_stage_ahead(AnyStageId(anystgInt)); } - } - exec_log(rExec, ExecContext::PipelineCancel{cancelPl, rCancelExecPl.stage}); + exec_log(rExec, ExecContext::PipelineCancel{cancelPl, rCancelExecPl.stage}); + } }); } @@ -690,8 +762,10 @@ static constexpr bool pipeline_can_advance(ExecPipeline &rExecPl) noexcept { // Pipeline can advance if... return + rExecPl.running + // Tasks required by stage are done - rExecPl.ownStageReqTasksLeft == 0 + && rExecPl.ownStageReqTasksLeft == 0 // Not required by any tasks && rExecPl.tasksReqOwnStageLeft == 0 diff --git a/src/osp/tasks/execute.h b/src/osp/tasks/execute.h index 703d001b..c79400cb 100644 --- a/src/osp/tasks/execute.h +++ b/src/osp/tasks/execute.h @@ -165,6 +165,12 @@ struct ExecLog PipelineId pipeline; }; + struct ExternalSignal + { + PipelineId pipeline; + bool ignored; + }; + using LogMsg_t = std::variant< UpdateStart, UpdateCycle, @@ -179,12 +185,12 @@ struct ExecLog EnqueueTaskReq, UnblockTask, CompleteTask, - ExternalRunRequest>; + ExternalRunRequest, + ExternalSignal>; std::vector logMsg; bool doLogging{true}; -}; - +}; // struct ExecLog /** * @brief State for executing Tasks and TaskGraph diff --git a/src/osp/tasks/tasks.cpp b/src/osp/tasks/tasks.cpp index d6ad658b..535c4437 100644 --- a/src/osp/tasks/tasks.cpp +++ b/src/osp/tasks/tasks.cpp @@ -48,7 +48,7 @@ struct PipelineCounts { std::array stageCounts; - uint8_t stages {0}; + uint8_t stages { 0 }; PipelineId firstChild { lgrn::id_null() }; PipelineId sibling { lgrn::id_null() }; @@ -84,6 +84,12 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView rStageCount = std::max(rStageCount, uint8_t(uint8_t(stage) + 1)); }; + // Max 1 stage for each valid pipeline. Supporting 0-stage pipelines take more effort + for (PipelineInt const plInt : tasks.m_pipelineIds.bitview().zeros()) + { + plCounts[PipelineId(plInt)].stages = 1; + } + // Count stages from task run-ons for (TaskInt const taskInt : tasks.m_taskIds.bitview().zeros()) { @@ -105,9 +111,9 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView } } - for (PipelineCounts const& plCount : plCounts) + for (PipelineInt const plInt : tasks.m_pipelineIds.bitview().zeros()) { - totalStages += plCount.stages; + totalStages += plCounts[PipelineId(plInt)].stages; } // 2. Count TaskRequiresStages and StageRequiresTasks @@ -354,7 +360,7 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView descendantCount += 1 + childDescendantCount; child = rChildCounts.sibling; - ++ childPos; + childPos += 1 + childDescendantCount; } out.pltreeDescendantCounts[pos] = descendantCount; @@ -381,7 +387,6 @@ TaskGraph make_exec_graph(Tasks const& tasks, ArrayView rootPos += 1 + rootDescendantCount; } - return out; } diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 9001fc45..37c1a729 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -45,6 +45,18 @@ return osp::arrayView( arr.data(), arr.size() ); \ } +#define OSP_DECLARE_STAGE_SCHEDULE(type, schedule_enum) \ + constexpr inline type stage_schedule([[maybe_unused]] type _) noexcept \ + { \ + return schedule_enum; \ + } + +#define OSP_DECLARE_STAGE_NO_SCHEDULE(type) \ + constexpr inline type stage_schedule([[maybe_unused]] type _) noexcept \ + { \ + return lgrn::id_null(); \ + } + namespace osp { @@ -78,6 +90,7 @@ struct PipelineInfo struct PipelineControl { + TaskId scheduler { lgrn::id_null() }; StageId waitStage { lgrn::id_null() }; bool isLoopScope { false }; }; @@ -201,7 +214,8 @@ struct TaskGraph // not yet used //lgrn::IntArrayMultiMap taskAcquire; /// Tasks acquire (n) Semaphores //lgrn::IntArrayMultiMap semaAcquiredBy; /// Semaphores are acquired by (n) Tasks -}; + +}; // struct TaskGraph TaskGraph make_exec_graph(Tasks const& tasks, ArrayView data); @@ -310,6 +324,4 @@ struct PipelineDef using PipelineDefBlank_t = PipelineDef; - - } // namespace osp diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index b84d3481..17b76f30 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -43,23 +43,11 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec { std::vector topDataRefs; - std::cout << "-- Top Run Blocking called\n"; - - top_write_log(std::cout, tasks, rTaskData, graph, rExec); - rExec.logMsg.clear(); - - top_write_pipeline_states(std::cout, tasks, rTaskData, graph, rExec); - // Run until there's no tasks left to run while (true) { - auto const runTasksLeft = rExec.tasksQueuedRun.size(); - auto const blockedTasksLeft = rExec.tasksQueuedBlocked.size(); - - if (runTasksLeft+blockedTasksLeft == 0) - { - break; - } + auto const runTasksLeft = rExec.tasksQueuedRun.size(); + //auto const blockedTasksLeft = rExec.tasksQueuedBlocked.size(); if (runTasksLeft != 0) { @@ -77,23 +65,17 @@ void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec bool const shouldRun = (rTopTask.m_func != nullptr); - // Task actually runs here + // Task function is called here TaskActions const status = shouldRun ? rTopTask.m_func(worker, topDataRefs) : TaskActions{}; complete_task(tasks, graph, rExec, task, status); - top_write_log(std::cout, tasks, rTaskData, graph, rExec); - rExec.logMsg.clear(); } else { - std::cout << "RIP pipelines. something deadlocked UwU\n"; - top_write_pipeline_states(std::cout, tasks, rTaskData, graph, rExec); - std::abort(); + break; } exec_update(tasks, graph, rExec); - top_write_log(std::cout, tasks, rTaskData, graph, rExec); - rExec.logMsg.clear(); } } @@ -114,20 +96,53 @@ static void write_task_requirements(std::ostream &rStream, Tasks const& tasks, T } -void top_write_pipeline_states(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t const& taskData, TaskGraph const& graph, ExecContext const& exec) +std::ostream& operator<<(std::ostream& rStream, TopExecWriteState const& write) { - constexpr int nameMinColumns = 48; + auto const& [tasks, taskData, graph, exec] = write; + + static constexpr int nameMinColumns = 50; + static constexpr int maxDepth = 4; - for (PipelineInt plInt : tasks.m_pipelineIds.bitview().zeros()) + rStream << "Pipeline/Tree | Status | Stages | Pipeline Names\n" + << "_________________________________________________________________________________________\n"; + + auto const write_pipeline = [&rStream, &tasks=tasks, &exec=exec, &graph=graph] (PipelineId const pipeline, int const depth) { - auto const pl = PipelineId(plInt); - ExecPipeline const &plExec = exec.plData[pl]; + ExecPipeline const &plExec = exec.plData[pipeline]; + + for (int i = 0; i < depth; ++i) + { + rStream << "- "; + } + + rStream << "PL" << std::setw(3) << std::left << PipelineInt(pipeline) << " "; + + for (int i = 0; i < (maxDepth - depth); ++i) + { + rStream << " "; + } + + rStream << " | "; - rStream << "PL" << std::setw(3) << std::left << plInt << ": "; + ExecPipeline const &execPl = exec.plData[pipeline]; - int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pl); + bool const signalBlocked = (execPl.waitStage != lgrn::id_null()) + && (execPl.waitStage == execPl.stage) + && ( ! execPl.waitSignaled ); - PipelineInfo const& info = tasks.m_pipelineInfo[pl]; + rStream << (execPl.running ? 'R' : '-') + << (execPl.loop ? 'L' : '-') + << (execPl.loopChildrenLeft != 0 ? 'O' : '-') + << (execPl.canceled ? 'C' : '-') + << (signalBlocked ? 'S' : '-') + << (execPl.tasksQueuedRun != 0 ? 'Q' : '-') + << (execPl.tasksQueuedBlocked != 0 ? 'B' : '-'); + + rStream << " | "; + + int const stageCount = fanout_size(graph.pipelineToFirstAnystg, pipeline); + + PipelineInfo const& info = tasks.m_pipelineInfo[pipeline]; auto const stageNames = ArrayView{PipelineInfo::sm_stageNames[info.stageType]}; @@ -140,7 +155,7 @@ void top_write_pipeline_states(std::ostream &rStream, Tasks const& tasks, TopTas << stageNames[stage] << (sel ? ']' : ' '); - charsUsed += 4 + stageNames[stage].size(); + charsUsed += 2 + stageNames[stage].size(); } for (; charsUsed < nameMinColumns; ++charsUsed) @@ -148,11 +163,39 @@ void top_write_pipeline_states(std::ostream &rStream, Tasks const& tasks, TopTas rStream << ' '; } - rStream << "- " << info.name; + rStream << " | " << info.name; rStream << "\n"; + }; + + auto const traverse = [&write_pipeline, &rStream, &graph=graph] (auto const& self, PipelineTreePos_t first, PipelineTreePos_t last, int depth) -> void + { + uint32_t descendants = 0; + for (PipelineTreePos_t pos = first; pos != last; pos += 1 + descendants) + { + descendants = graph.pltreeDescendantCounts[pos]; + + write_pipeline(graph.pltreeToPipeline[pos], depth); + + self(self, pos + 1, pos + 1 + descendants, depth + 1); + } + }; + + traverse(traverse, 0, graph.pltreeToPipeline.size(), 0); + + // Write pipelines that are not in the tree + for (PipelineInt const plInt : tasks.m_pipelineIds.bitview().zeros()) + { + auto const pipeline = PipelineId(plInt); + if (graph.pipelineToPltree[pipeline] == lgrn::id_null()) + { + write_pipeline(pipeline, 0); + } } + rStream << "*Status: [R: Running] [L: Looping] [O: Looping Children] [C: Canceled] [S: Signal Blocked] [Q: Has Queued Tasks To Run] [B: Queued Tasks Blocked]\n"; + + for (auto const [task, block] : exec.tasksQueuedBlocked.each()) { rStream << "Task Blocked: " << "TASK" << TaskInt(task) << " - " << taskData[task].m_debugName << "\n"; @@ -160,19 +203,28 @@ void top_write_pipeline_states(std::ostream &rStream, Tasks const& tasks, TopTas write_task_requirements(rStream, tasks, graph, exec, task); } - // [*run*] - [ not ] - *trig* + return rStream; } -void top_write_log(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t const& taskData, TaskGraph const& graph, ExecContext const& exec) +std::ostream& operator<<(std::ostream& rStream, TopExecWriteLog const& write) { - auto const stage_name = [&tasks] (PipelineId pl, StageId stg) -> std::string_view + auto const& [tasks, taskData, graph, exec] = write; + + auto const stage_name = [&tasks=tasks] (PipelineId pl, StageId stg) -> std::string_view { - PipelineInfo const& info = tasks.m_pipelineInfo[pl]; - auto const stageNames = ArrayView{PipelineInfo::sm_stageNames[info.stageType]}; - return stageNames[std::size_t(stg)]; + if (stg != lgrn::id_null()) + { + PipelineInfo const& info = tasks.m_pipelineInfo[pl]; + auto const stageNames = ArrayView{PipelineInfo::sm_stageNames[info.stageType]}; + return stageNames[std::size_t(stg)]; + } + else + { + return "NULL"; + } }; - auto const visitMsg = [&rStream, &tasks, &taskData, &graph, &stage_name] (auto&& msg) + auto const visitMsg = [&rStream, &tasks=tasks, &taskData=taskData, &graph=graph, &stage_name] (auto&& msg) { using MSG_T = std::decay_t; if constexpr (std::is_same_v) @@ -238,12 +290,18 @@ void top_write_log(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t c { rStream << "ExternalRunRequest PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; } + else if constexpr (std::is_same_v) + { + rStream << "ExternalSignal PL" << std::setw(3) << PipelineInt(msg.pipeline) << (msg.ignored ? " IGNORED!" : " ") << "\n"; + } }; for (ExecContext::LogMsg_t const& msg : exec.logMsg) { std::visit(visitMsg, msg); } + + return rStream; } diff --git a/src/osp/tasks/top_execute.h b/src/osp/tasks/top_execute.h index 490ec228..35a74d73 100644 --- a/src/osp/tasks/top_execute.h +++ b/src/osp/tasks/top_execute.h @@ -35,8 +35,24 @@ namespace osp void top_run_blocking(Tasks const& tasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, WorkerContext worker = {}); -void top_write_pipeline_states(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t const& taskData, TaskGraph const& graph, ExecContext const& exec); +struct TopExecWriteState +{ + Tasks const &tasks; + TopTaskDataVec_t const &taskData; + TaskGraph const &graph; + ExecContext const &exec; +}; + +struct TopExecWriteLog +{ + Tasks const &tasks; + TopTaskDataVec_t const &taskData; + TaskGraph const &graph; + ExecContext const &exec; +}; + +std::ostream& operator<<(std::ostream& rStream, TopExecWriteState const& write); -void top_write_log(std::ostream &rStream, Tasks const& tasks, TopTaskDataVec_t const& taskData, TaskGraph const& graph, ExecContext const& exec); +std::ostream& operator<<(std::ostream& rStream, TopExecWriteLog const& write); } // namespace testapp diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index 93a78af6..a17a1839 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -122,11 +122,12 @@ struct Session std::vector m_pipelines; std::vector m_tasks; - TplPipelineStage m_cleanup{ lgrn::id_null(), lgrn::id_null() }; + PipelineId m_cleanup { lgrn::id_null() }; std::size_t m_structHash{0}; std::string m_structName; -}; + +}; // struct Session struct SessionGroup { diff --git a/src/osp/tasks/top_tasks.h b/src/osp/tasks/top_tasks.h index 734d2700..9ef585a4 100644 --- a/src/osp/tasks/top_tasks.h +++ b/src/osp/tasks/top_tasks.h @@ -33,14 +33,11 @@ namespace osp { -// TODO: ignore debug names on release builds - struct TopTask { std::string m_debugName; std::vector m_dataUsed; TopTaskFunc_t m_func { nullptr }; - //bool m_awareOfDirtyDeps { false }; }; using TopTaskDataVec_t = KeyedVec; diff --git a/src/test_application/ActiveApplication.cpp b/src/test_application/MagnumApplication.cpp similarity index 89% rename from src/test_application/ActiveApplication.cpp rename to src/test_application/MagnumApplication.cpp index 937ccd33..f3988032 100644 --- a/src/test_application/ActiveApplication.cpp +++ b/src/test_application/MagnumApplication.cpp @@ -23,7 +23,7 @@ * SOFTWARE. */ -#include "ActiveApplication.h" +#include "MagnumApplication.h" #include "osp/types.h" #include @@ -34,8 +34,8 @@ using namespace testapp; -using Key_t = ActiveApplication::KeyEvent::Key; -using Mouse_t = ActiveApplication::MouseEvent::Button; +using Key_t = MagnumApplication::KeyEvent::Key; +using Mouse_t = MagnumApplication::MouseEvent::Button; using osp::input::sc_keyboard; using osp::input::sc_mouse; @@ -48,7 +48,7 @@ using osp::input::EVarOperator; using Magnum::Platform::Application; -ActiveApplication::ActiveApplication(const Application::Arguments& arguments, +MagnumApplication::MagnumApplication(const Application::Arguments& arguments, UserInputHandler& rUserInput) : Application{arguments, Configuration{}.setTitle("OSP-Magnum").setSize({1280, 720})} , m_rUserInput(rUserInput) @@ -59,18 +59,18 @@ ActiveApplication::ActiveApplication(const Application::Arguments& arguments, m_timeline.start(); } -ActiveApplication::~ActiveApplication() +MagnumApplication::~MagnumApplication() { - m_onDraw = {}; + m_ospApp.reset(nullptr); } -void ActiveApplication::drawEvent() +void MagnumApplication::drawEvent() { m_rUserInput.update_controls(); - if (m_onDraw.operator bool()) + if (m_ospApp != nullptr) { - m_onDraw(*this, m_timeline.previousFrameDuration()); + m_ospApp->draw(*this, m_timeline.previousFrameDuration()); } m_rUserInput.clear_events(); @@ -80,38 +80,38 @@ void ActiveApplication::drawEvent() redraw(); } -void ActiveApplication::keyPressEvent(KeyEvent& event) +void MagnumApplication::keyPressEvent(KeyEvent& event) { if (event.isRepeated()) { return; } m_rUserInput.event_raw(osp::input::sc_keyboard, (int) event.key(), osp::input::EButtonEvent::Pressed); } -void ActiveApplication::keyReleaseEvent(KeyEvent& event) +void MagnumApplication::keyReleaseEvent(KeyEvent& event) { if (event.isRepeated()) { return; } m_rUserInput.event_raw(osp::input::sc_keyboard, (int) event.key(), osp::input::EButtonEvent::Released); } -void ActiveApplication::mousePressEvent(MouseEvent& event) +void MagnumApplication::mousePressEvent(MouseEvent& event) { m_rUserInput.event_raw(osp::input::sc_mouse, (int) event.button(), osp::input::EButtonEvent::Pressed); } -void ActiveApplication::mouseReleaseEvent(MouseEvent& event) +void MagnumApplication::mouseReleaseEvent(MouseEvent& event) { m_rUserInput.event_raw(osp::input::sc_mouse, (int) event.button(), osp::input::EButtonEvent::Released); } -void ActiveApplication::mouseMoveEvent(MouseMoveEvent& event) +void MagnumApplication::mouseMoveEvent(MouseMoveEvent& event) { m_rUserInput.mouse_delta(event.relativePosition()); } -void ActiveApplication::mouseScrollEvent(MouseScrollEvent & event) +void MagnumApplication::mouseScrollEvent(MouseScrollEvent & event) { m_rUserInput.scroll_delta(static_cast(event.offset())); } @@ -221,9 +221,9 @@ const std::map> gc_buttonMap = { {"F12", {sc_keyboard, (int)Key_t::F12 }}, //Mouse - {"RMouse", {sc_mouse, (int)ActiveApplication::MouseEvent::Button::Right }}, - {"LMouse", {sc_mouse, (int)ActiveApplication::MouseEvent::Button::Left }}, - {"MMouse", {sc_mouse, (int)ActiveApplication::MouseEvent::Button::Middle }} + {"RMouse", {sc_mouse, (int)MagnumApplication::MouseEvent::Button::Right }}, + {"LMouse", {sc_mouse, (int)MagnumApplication::MouseEvent::Button::Left }}, + {"MMouse", {sc_mouse, (int)MagnumApplication::MouseEvent::Button::Middle }} }; ControlExprConfig_t parse_control(std::string_view str) noexcept diff --git a/src/test_application/ActiveApplication.h b/src/test_application/MagnumApplication.h similarity index 68% rename from src/test_application/ActiveApplication.h rename to src/test_application/MagnumApplication.h index 31a5402f..dc60483a 100644 --- a/src/test_application/ActiveApplication.h +++ b/src/test_application/MagnumApplication.h @@ -34,33 +34,44 @@ #include // workaround: memcpy needed by SDL2 #include -#include #include namespace testapp { -class ActiveApplication; +class MagnumApplication; -// Stored inside an ActiveApplicaton to use as a main draw function. -// Renderer state can be stored in lambda capture -using on_draw_t = std::function; +class IOspApplication +{ +public: + virtual ~IOspApplication() = default; + + virtual void run(MagnumApplication& rApp) = 0; + virtual void draw(MagnumApplication& rApp, float delta) = 0; + virtual void exit(MagnumApplication& rApp) = 0; +}; /** * @brief An interactive Magnum application * * This is intended to run a flight scene, map view, vehicle editor, or menu. */ -class ActiveApplication : public Magnum::Platform::Application +class MagnumApplication : Magnum::Platform::Application { public: - explicit ActiveApplication( + using Magnum::Platform::Application::KeyEvent; + using Magnum::Platform::Application::MouseEvent; + using Magnum::Platform::Application::Arguments; + + using AppPtr_t = std::unique_ptr; + + explicit MagnumApplication( const Magnum::Platform::Application::Arguments& arguments, osp::input::UserInputHandler& rUserInput); - ~ActiveApplication(); + ~MagnumApplication(); void keyPressEvent(KeyEvent& event) override; void keyReleaseEvent(KeyEvent& event) override; @@ -70,16 +81,28 @@ class ActiveApplication : public Magnum::Platform::Application void mouseMoveEvent(MouseMoveEvent& event) override; void mouseScrollEvent(MouseScrollEvent& event) override; - void set_on_draw(on_draw_t onDraw) + void exec() { - m_onDraw = std::move(onDraw); + m_ospApp->run(*this); + Magnum::Platform::Application::exec(); + m_ospApp->exit(*this); + } + + void exit() + { + Magnum::Platform::Application::exit(); + } + + void set_osp_app(AppPtr_t ospApp) + { + m_ospApp = std::move(ospApp); } private: void drawEvent() override; - on_draw_t m_onDraw; + AppPtr_t m_ospApp; osp::input::UserInputHandler &m_rUserInput; @@ -92,11 +115,11 @@ void config_controls(osp::input::UserInputHandler& rUserInput); } /** -* Parses the control string from the config file. -* -* A "None" input returns a empty vector. -* -* @param Control string -* @returns vector of the control created from the string. -*/ + * Parses the control string from the config file. + * + * A "None" input returns a empty vector. + * + * @param Control string + * @returns vector of the control created from the string. + */ osp::input::ControlExprConfig_t parse_control(std::string_view str) noexcept; diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 6a6bb6bf..f0a46393 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -34,11 +34,13 @@ namespace testapp enum class EStgOptn : uint8_t { + ModifyOrSignal, Schedule, Run, Done }; -OSP_DECLARE_STAGE_NAMES(EStgOptn, "Schedule", "Run", "Done"); +OSP_DECLARE_STAGE_NAMES(EStgOptn, "Modify/Signal", "Schedule", "Run", "Done"); +OSP_DECLARE_STAGE_SCHEDULE(EStgOptn, EStgOptn::Schedule); enum class EStgEvnt : uint8_t @@ -47,15 +49,7 @@ enum class EStgEvnt : uint8_t Done_ }; OSP_DECLARE_STAGE_NAMES(EStgEvnt, "Run", "Done"); - - -enum class EStgGate : uint8_t -{ - Wait, - Open -}; -OSP_DECLARE_STAGE_NAMES(EStgGate, "Wait", "Open"); - +OSP_DECLARE_STAGE_NO_SCHEDULE(EStgEvnt); /** * @brief Temporary queue / events that are filled, used, then cleared right away @@ -68,7 +62,8 @@ enum class EStgIntr : uint8_t UseOrRun, Clear }; -OSP_DECLARE_STAGE_NAMES(EStgIntr, "Resize", "Modify", "Schedule", "Use", "Clear"); +OSP_DECLARE_STAGE_NAMES(EStgIntr, "Resize", "Modify", "Schedule", "Use/Run", "Clear"); +OSP_DECLARE_STAGE_SCHEDULE(EStgIntr, EStgIntr::Schedule_); /** @@ -92,7 +87,7 @@ enum class EStgCont : uint8_t ///< Container is ready to use }; OSP_DECLARE_STAGE_NAMES(EStgCont, "Delete", "New", "Modify", "Use"); - +OSP_DECLARE_STAGE_NO_SCHEDULE(EStgCont); enum class EStgFBO { @@ -101,14 +96,14 @@ enum class EStgFBO Unbind }; OSP_DECLARE_STAGE_NAMES(EStgFBO, "Bind", "Draw", "Unbind"); - +OSP_DECLARE_STAGE_NO_SCHEDULE(EStgFBO); using osp::PipelineDef; //----------------------------------------------------------------------------- -#define TESTAPP_DATA_APPLICATION 1, \ - idResources +#define TESTAPP_DATA_APPLICATION 2, \ + idResources, idMainLoopCtrl struct PlApplication { PipelineDef mainLoop {"mainLoop - ..."}; @@ -121,7 +116,7 @@ struct PlApplication struct PlScene { PipelineDef cleanup {"cleanup - Scene cleanup before destruction"}; - PipelineDef update {"update - main loop "}; + PipelineDef update {"update - main loop "}; }; #define TESTAPP_DATA_COMMON_SCENE 6, \ @@ -297,7 +292,7 @@ struct PlNewton idUserInput struct PlWindowApp { - PipelineDef inputs {"render"}; + PipelineDef inputs {"inputs"}; }; @@ -308,6 +303,8 @@ struct PlMagnum { PipelineDef cleanup {"cleanup Cleanup Magnum"}; + PipelineDef sync {"render"}; + PipelineDef meshGL {"meshGL"}; PipelineDef textureGL {"textureGL"}; @@ -322,8 +319,8 @@ struct PlMagnum struct PlSceneRenderer { PipelineDef render {"render"}; - PipelineDef fbo {"fboRender"}; + PipelineDef fbo {"fboRender"}; PipelineDef scnRender {"scnRender"}; PipelineDef group {"group"}; PipelineDef groupEnts {"groupEnts"}; diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index a4308636..b408e136 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -35,7 +35,7 @@ #include "scene_universe.h" #include "scene_vehicles.h" -#include "../ActiveApplication.h" +#include "../MagnumApplication.h" #include "../VehicleBuilder.h" #include @@ -61,53 +61,95 @@ static constexpr auto sc_matFlat = active::MaterialId(1); static constexpr auto sc_matPhong = active::MaterialId(2); static constexpr int sc_materialCount = 4; +struct CommonMagnumApp : IOspApplication +{ + CommonMagnumApp(TestApp &rTestApp, MainLoopControl &rMainLoopCtrl, PipelineId mainLoop, PipelineId renderSync, PipelineId sceneUpdate, PipelineId sceneRender) noexcept + : m_rTestApp { rTestApp } + , m_rMainLoopCtrl { rMainLoopCtrl } + , m_mainLoop { mainLoop } + , m_renderSync { renderSync } + , m_sceneUpdate { sceneUpdate } + , m_sceneRender { sceneRender } + + { } + + void run(MagnumApplication& rApp) override + { + // Start the main loop + + PipelineId const mainLoop = m_rTestApp.m_application.get_pipelines().mainLoop; + m_rTestApp.m_pExecutor->run(m_rTestApp, mainLoop); + } + + void draw(MagnumApplication& rApp, float delta) override + { + // Magnum Application's main loop is here + + m_rMainLoopCtrl = MainLoopControl{ + .doUpdate = true, + .doSync = true, + .doResync = false, + .doRender = true, + }; + + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_mainLoop); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_sceneUpdate); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_sceneRender); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_renderSync); + + m_rTestApp.m_pExecutor->wait(m_rTestApp); + } + + void exit(MagnumApplication& rApp) override + { + m_rMainLoopCtrl = MainLoopControl{ + .doUpdate = false, + .doSync = false, + .doResync = false, + .doRender = false, + }; + + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_mainLoop); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_sceneUpdate); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_sceneRender); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_renderSync); + + m_rTestApp.m_pExecutor->wait(m_rTestApp); + + if (m_rTestApp.m_pExecutor->is_running(m_rTestApp)) + { + // Main loop must have stopped, but didn't! + std::abort(); + } + } + + TestApp &m_rTestApp; + MainLoopControl &m_rMainLoopCtrl; + + PipelineId m_mainLoop; + PipelineId m_renderSync; + PipelineId m_sceneUpdate; + PipelineId m_sceneRender; +}; static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& scnRenderer, std::vector run = {}) { -// OSP_DECLARE_GET_DATA_IDS(scnRenderer, TESTAPP_DATA_COMMON_RENDERER); -// OSP_DECLARE_GET_DATA_IDS(rTestApp.m_windowApp, TESTAPP_DATA_WINDOW_APP); -// OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); - -// auto &rActiveApp = top_get(rTestApp.m_topData, idActiveApp); -// auto &rCamera = top_get(rTestApp.m_topData, idCamera); -// rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); - -// auto tgScn = scene .get_pipelines(); -// auto tgWin = rTestApp.m_windowApp .get_pipelines(); - -// // Resynchronize scene with new renderer -// exec_conform(rTestApp.m_tasks, rTestApp.m_exec); -// exec_request_run(rTestApp.m_exec, tgScn.resyncAll); -// exec_request_run(rTestApp.m_exec, tgScn.updDraw); -// exec_request_run(rTestApp.m_exec, tgScn.updActive); - -// run.insert(run.end(), { tgScn.updTime, tgScn.updActive, tgScn.updDraw, tgWin.inputs, tgWin.display}); - -// // run gets copied but who cares lol -// rActiveApp.set_on_draw( [&rTestApp, run = std::move(run), resync = tgScn.resyncAll] -// (ActiveApplication& rApp, float delta) -// { -// // Magnum Application's main loop is here - -// for (PipelineId const pipeline : run) -// { -// exec_request_run(rTestApp.m_exec, pipeline); -// } - -// exec_update(rTestApp.m_tasks, *rTestApp.m_graph, rTestApp.m_exec); - -// top_run_blocking(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec); - - // Enqueued tasks that don't run indicate a deadlock -// if ( ! std::all_of(std::begin(rExec.m_taskQueuedCounts), -// std::end(rExec.m_taskQueuedCounts), -// [] (unsigned int n) { return n == 0; })) -// { -// OSP_LOG_ERROR("Deadlock detected!"); -// debug_top_print_deadlock( rTasks, rTaskData, rExec); -// std::abort(); -// } -// }); + OSP_DECLARE_GET_DATA_IDS(scnRenderer, TESTAPP_DATA_COMMON_RENDERER); + OSP_DECLARE_GET_DATA_IDS(rTestApp.m_application, TESTAPP_DATA_APPLICATION); + OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); + + auto &rMainLoopCtrl = top_get (rTestApp.m_topData, idMainLoopCtrl); + auto &rActiveApp = top_get(rTestApp.m_topData, idActiveApp); + auto &rCamera = top_get (rTestApp.m_topData, idCamera); + + rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); + + PipelineId const mainLoop = rTestApp.m_application .get_pipelines() .mainLoop; + PipelineId const renderSync = rTestApp.m_magnum .get_pipelines() .sync; + PipelineId const sceneUpdate = scene .get_pipelines() .update; + PipelineId const sceneRender = scnRenderer .get_pipelines() .render; + + rActiveApp.set_osp_app( std::make_unique(rTestApp, rMainLoopCtrl, mainLoop, renderSync, sceneUpdate, sceneRender) ); } template @@ -125,7 +167,6 @@ static ScenarioMap_t make_scenarios() register_stage_enum(); register_stage_enum(); - register_stage_enum(); register_stage_enum(); register_stage_enum(); register_stage_enum(); @@ -142,7 +183,7 @@ static ScenarioMap_t make_scenarios() rOut.m_sessions.resize(1); TopDataId const idSceneData = rOut.m_sessions[0].acquire_data<1>(rTestApp.m_topData)[0]; - OSP_DECLARE_CREATE_DATA_IDS(rTestApp.m_application, rTestApp.m_topData, TESTAPP_DATA_APPLICATION); + OSP_DECLARE_GET_DATA_IDS(rTestApp.m_application, TESTAPP_DATA_APPLICATION); auto &rResources = top_get(rTestApp.m_topData, idResources); @@ -157,12 +198,12 @@ static ScenarioMap_t make_scenarios() OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); OSP_DECLARE_GET_DATA_IDS(rTestApp.m_windowApp, TESTAPP_DATA_WINDOW_APP); - auto &rActiveApp = top_get< ActiveApplication > (rTestApp.m_topData, idActiveApp); + auto &rActiveApp = top_get< MagnumApplication > (rTestApp.m_topData, idActiveApp); auto &rRenderGl = top_get< active::RenderGL > (rTestApp.m_topData, idRenderGl); auto &rUserInput = top_get< input::UserInputHandler >(rTestApp.m_topData, idUserInput); // Renderer state is stored as lambda capture - rActiveApp.set_on_draw(enginetest::generate_draw_func(rScene, rActiveApp, rRenderGl, rUserInput)); + rActiveApp.set_osp_app(enginetest::generate_draw_func(rScene, rActiveApp, rRenderGl, rUserInput)); }; }); @@ -182,7 +223,7 @@ static ScenarioMap_t make_scenarios() ] = resize_then_unpack<10>(rTestApp.m_scene.m_sessions); // Compose together lots of Sessions - scene = setup_scene (builder, rTopData); + scene = setup_scene (builder, rTopData, application); commonScene = setup_common_scene (builder, rTopData, scene, application, defaultPkg); physics = setup_physics (builder, rTopData, scene, commonScene); shapeSpawn = setup_shape_spawn (builder, rTopData, scene, commonScene, physics, sc_matVisualizer); @@ -197,9 +238,11 @@ static ScenarioMap_t make_scenarios() create_materials(rTopData, commonScene, sc_materialCount); add_floor(rTopData, application, commonScene, shapeSpawn, sc_matVisualizer, defaultPkg); - auto const tgScn = scene .get_pipelines(); - auto const tgCS = commonScene .get_pipelines(); - auto const tgShSp = shapeSpawn .get_pipelines(); +// auto const tgScn = scene .get_pipelines(); +// auto const tgCS = commonScene .get_pipelines(); +// auto const tgShSp = shapeSpawn .get_pipelines(); + + //exec_conform(rTestApp.m_tasks, rTestApp.m_exec); @@ -225,7 +268,7 @@ static ScenarioMap_t make_scenarios() cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, scnRender); cameraFree = setup_camera_free (builder, rTopData, windowApp, scene, cameraCtrl); shVisual = setup_shader_visualizer (builder, rTopData, magnum, scene, commonScene, scnRender, sc_matVisualizer); - //camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); + camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); setup_magnum_draw(rTestApp, scene, scnRender); }; diff --git a/src/test_application/activescenes/scenarios.h b/src/test_application/activescenes/scenarios.h index 656fad49..e55de350 100644 --- a/src/test_application/activescenes/scenarios.h +++ b/src/test_application/activescenes/scenarios.h @@ -41,7 +41,6 @@ namespace scenes { using enum EStgOptn; using enum EStgCont; - using enum EStgGate; using enum EStgIntr; using enum EStgEvnt; using enum EStgFBO; @@ -50,7 +49,8 @@ namespace scenes struct MainLoopControl { bool doUpdate; - bool doRenderResync; + bool doSync; + bool doResync; bool doRender; }; @@ -64,5 +64,4 @@ using ScenarioMap_t = std::unordered_map; ScenarioMap_t const& scenarios(); - } // namespace testapp diff --git a/src/test_application/activescenes/scenarios_enginetest.cpp b/src/test_application/activescenes/scenarios_enginetest.cpp index 31d24984..bf5b0528 100644 --- a/src/test_application/activescenes/scenarios_enginetest.cpp +++ b/src/test_application/activescenes/scenarios_enginetest.cpp @@ -24,7 +24,7 @@ */ #include "CameraController.h" -#include "../ActiveApplication.h" +#include "../MagnumApplication.h" #include #include @@ -203,7 +203,7 @@ struct EngineTestRenderer // Support for assigning render-space GL meshes/textures and transforms // for ActiveEnts - osp::active::ACtxSceneRenderGL m_renderGl{}; + osp::active::ACtxSceneRenderGL m_sceneRenderGL{}; // Pre-built easy camera controls osp::active::Camera m_cam; @@ -231,16 +231,16 @@ void sync_test_scene( using namespace osp::active; using namespace osp::shader; - rRenderer.m_renderGl.m_drawTransform.resize(rScene.m_drawing.m_drawIds.capacity()); - rRenderer.m_renderGl.m_diffuseTexId.resize(rScene.m_drawing.m_drawIds.capacity()); - rRenderer.m_renderGl.m_meshId.resize(rScene.m_drawing.m_drawIds.capacity()); + rRenderer.m_sceneRenderGL.m_drawTransform.resize(rScene.m_drawing.m_drawIds.capacity()); + rRenderer.m_sceneRenderGL.m_diffuseTexId.resize(rScene.m_drawing.m_drawIds.capacity()); + rRenderer.m_sceneRenderGL.m_meshId.resize(rScene.m_drawing.m_drawIds.capacity()); // Assign or remove phong shaders from entities marked dirty sync_phong( std::cbegin(rScene.m_matPhongDirty), std::cend(rScene.m_matPhongDirty), rScene.m_matPhong, &rRenderer.m_groupFwdOpaque.m_entities, nullptr, - rScene.m_drawing.m_drawBasic, rRenderer.m_renderGl.m_diffuseTexId, + rScene.m_drawing.m_drawBasic, rRenderer.m_sceneRenderGL.m_diffuseTexId, rRenderer.m_phong); // Load required meshes and textures into OpenGL @@ -250,12 +250,12 @@ void sync_test_scene( // Assign GL meshes to entities with a mesh component SysRenderGL::assign_meshes( rScene.m_drawing.m_mesh, rScene.m_drawingRes.m_meshToRes, rScene.m_drawing.m_meshDirty, - rRenderer.m_renderGl.m_meshId, rRenderGl); + rRenderer.m_sceneRenderGL.m_meshId, rRenderGl); // Assign GL textures to entities with a texture component SysRenderGL::assign_textures( rScene.m_drawing.m_diffuseTex, rScene.m_drawingRes.m_texToRes, rScene.m_drawing.m_diffuseDirty, - rRenderer.m_renderGl.m_diffuseTexId, rRenderGl); + rRenderer.m_sceneRenderGL.m_diffuseTexId, rRenderGl); // Calculate hierarchy transforms @@ -265,7 +265,7 @@ void sync_test_scene( rScene.m_basic.m_scnGraph, rScene.m_drawing.m_activeToDraw, rScene.m_basic.m_transform, - rRenderer.m_renderGl.m_drawTransform, + rRenderer.m_sceneRenderGL.m_drawTransform, rScene.m_drawing.m_needDrawTf, drawTfDirty.begin(), drawTfDirty.end()); @@ -310,25 +310,61 @@ void render_test_scene( SysRenderGL::display_texture(rRenderGl, rFboColor); } -on_draw_t generate_draw_func(EngineTestScene& rScene, ActiveApplication &rApp, RenderGL& rRenderGl, UserInputHandler& rUserInput) +class EngineTestApp : public IOspApplication +{ +public: + EngineTestApp(EngineTestRenderer renderer, EngineTestScene& rScene, RenderGL& rRenderGl) + : m_renderer {std::move(renderer)} + , m_rScene {rScene} + , m_rRenderGl {rRenderGl} + { } + + ~EngineTestApp() override + { }; + + void run(MagnumApplication& rApp) override + { } + + void draw(MagnumApplication& rApp, float delta) override + { + update_test_scene(m_rScene, delta); + + // Rotate and move the camera based on user inputs + SysCameraController::update_view(m_renderer.m_camCtrl, delta); + SysCameraController::update_move(m_renderer.m_camCtrl, delta, true); + m_renderer.m_cam.m_transform = m_renderer.m_camCtrl.m_transform; + + sync_test_scene (m_rRenderGl, m_rScene, m_renderer); + render_test_scene(m_rRenderGl, m_rScene, m_renderer); + } + + void exit(MagnumApplication& rApp) override + { } + + EngineTestRenderer m_renderer; + + EngineTestScene &m_rScene; + RenderGL &m_rRenderGl; +}; + +MagnumApplication::AppPtr_t generate_draw_func(EngineTestScene& rScene, MagnumApplication &rApp, RenderGL& rRenderGl, UserInputHandler& rUserInput) { using namespace osp::active; using namespace osp::shader; - // Create renderer data. This uses a shared_ptr to allow being stored - // inside an std::function, which require copyable types - std::shared_ptr pRenderer - = std::make_shared(rUserInput); + auto pApp = std::make_unique(EngineTestRenderer{rUserInput}, rScene, rRenderGl); + + EngineTestRenderer &rRenderer = pApp->m_renderer; // Create Phong shaders auto const texturedFlags = Phong::Flag::DiffuseTexture | Phong::Flag::AlphaMask | Phong::Flag::AmbientTexture; - pRenderer->m_phong.m_shaderDiffuse = Phong{Phong::Configuration{}.setFlags(texturedFlags).setLightCount(2)}; - pRenderer->m_phong.m_shaderUntextured = Phong{Phong::Configuration{}.setLightCount(2)}; - pRenderer->m_phong.assign_pointers(pRenderer->m_renderGl, rRenderGl); + rRenderer.m_phong.m_shaderDiffuse = Phong{Phong::Configuration{}.setFlags(texturedFlags).setLightCount(2)}; + rRenderer.m_phong.m_shaderUntextured = Phong{Phong::Configuration{}.setLightCount(2)}; + rRenderer.m_phong.assign_pointers(rRenderer.m_sceneRenderGL, rRenderGl); - pRenderer->m_cam.set_aspect_ratio( + rRenderer.m_cam.set_aspect_ratio( osp::Vector2(Magnum::GL::defaultFramebuffer.viewport().size())); // Set all drawing stuff dirty then sync with renderer. @@ -339,21 +375,9 @@ on_draw_t generate_draw_func(EngineTestScene& rScene, ActiveApplication &rApp, R rScene.m_matPhongDirty.push_back(DrawEnt(entInt)); } - sync_test_scene(rRenderGl, rScene, *pRenderer); - - return [&rScene, pRenderer = std::move(pRenderer), &rRenderGl] ( - ActiveApplication& rApp, float delta) - { - update_test_scene(rScene, delta); - - // Rotate and move the camera based on user inputs - SysCameraController::update_view(pRenderer->m_camCtrl, delta); - SysCameraController::update_move(pRenderer->m_camCtrl, delta, true); - pRenderer->m_cam.m_transform = pRenderer->m_camCtrl.m_transform; + sync_test_scene(rRenderGl, rScene, rRenderer); - sync_test_scene(rRenderGl, rScene, *pRenderer); - render_test_scene(rRenderGl, rScene, *pRenderer); - }; + return pApp; } } // namespace testapp::enginetest diff --git a/src/test_application/activescenes/scenarios_enginetest.h b/src/test_application/activescenes/scenarios_enginetest.h index f1d18d13..b3e48083 100644 --- a/src/test_application/activescenes/scenarios_enginetest.h +++ b/src/test_application/activescenes/scenarios_enginetest.h @@ -24,7 +24,7 @@ */ #pragma once -#include "../ActiveApplication.h" +#include "../MagnumApplication.h" #include @@ -44,17 +44,17 @@ struct EngineTestScene; entt::any setup_scene(osp::Resources& rResources, osp::PkgId pkg); /** - * @brief Generate ActiveApplication draw function + * @brief Generate MagnumApplication draw function * * This draw function stores renderer data, and is responsible for updating * and drawing the engine test scene. * * @param rScene [ref] Engine test scene. Must be in stable memory. - * @param rApp [ref] Existing ActiveApplication to use GL resources of + * @param rApp [ref] Existing MagnumApplication to use GL resources of * - * @return ActiveApplication draw function + * @return MagnumApplication draw function */ -on_draw_t generate_draw_func(EngineTestScene& rScene, ActiveApplication& rApp, osp::active::RenderGL& rRenderGl, osp::input::UserInputHandler& rUserInput); +MagnumApplication::AppPtr_t generate_draw_func(EngineTestScene& rScene, MagnumApplication& rApp, osp::active::RenderGL& rRenderGl, osp::input::UserInputHandler& rUserInput); } // namespace testapp::enginetest diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index b99b928a..764a0037 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -43,14 +43,31 @@ namespace testapp::scenes Session setup_scene( TopTaskBuilder& rBuilder, - ArrayView const topData) + ArrayView const topData, + Session const& application) { + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); + auto const tgApp = application.get_pipelines< PlApplication >(); + osp::Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_SCENE); top_emplace< float >(topData, idDeltaTimeIn, 1.0f / 60.0f); - out.create_pipelines(rBuilder); + auto const plScn = out.create_pipelines(rBuilder); + + rBuilder.pipeline(plScn.update).parent(tgApp.mainLoop).wait_for_signal(ModifyOrSignal); + + rBuilder.task() + .name ("Schedule Scene update") + .schedules ({plScn.update(Schedule)}) + .push_to (out.m_tasks) + .args ({ idMainLoopCtrl}) + .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions + { + return rMainLoopCtrl.doUpdate ? osp::TaskActions{} : osp::TaskAction::Cancel; + }); + return out; } diff --git a/src/test_application/activescenes/scene_common.h b/src/test_application/activescenes/scene_common.h index ef7f9f43..7dd2331c 100644 --- a/src/test_application/activescenes/scene_common.h +++ b/src/test_application/activescenes/scene_common.h @@ -50,7 +50,8 @@ struct NamedMeshes osp::Session setup_scene( osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData); + osp::ArrayView topData, + osp::Session const& application); /** * @brief Support for Time, ActiveEnts, Hierarchy, Transforms, Drawing, and more... diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index 9bf4db98..55a74b84 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -153,7 +153,7 @@ Session setup_camera_ctrl( OSP_DECLARE_GET_DATA_IDS(scnRender, TESTAPP_DATA_COMMON_RENDERER); auto const tgSR = scnRender.get_pipelines(); - auto const tgWin = windowApp.get_pipelines(); + //auto const tgWin = windowApp.get_pipelines(); auto &rUserInput = top_get< osp::input::UserInputHandler >(topData, idUserInput); @@ -163,6 +163,8 @@ Session setup_camera_ctrl( top_emplace< ACtxCameraController > (topData, idCamCtrl, rUserInput); + rBuilder.pipeline(tgCmCt.camCtrl).parent(tgSR.render); + rBuilder.task() .name ("Position Rendering Camera according to Camera Controller") .run_on ({tgSR.render(Run)}) diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index d8700781..7e6d8343 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -64,6 +64,8 @@ Session setup_physics( OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_PHYSICS); auto const tgPhy = out.create_pipelines(rBuilder); + rBuilder.pipeline(tgPhy.physics).parent(tgScn.update); + top_emplace< ACtxPhysics > (topData, idPhys); rBuilder.task() @@ -98,6 +100,9 @@ Session setup_shape_spawn( OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_SHAPE_SPAWN); auto const tgShSp = out.create_pipelines(rBuilder); + rBuilder.pipeline(tgShSp.spawnRequest) .parent(tgScn.update); + rBuilder.pipeline(tgShSp.spawnedEnts) .parent(tgScn.update); + top_emplace< ACtxShapeSpawner > (topData, idSpawner, ACtxShapeSpawner{ .m_materialId = materialId }); rBuilder.task() .name ("Create entities for requested shapes to spawn") @@ -107,7 +112,7 @@ Session setup_shape_spawn( .args ({ idBasic, idSpawner }) .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept { - LGRN_ASSERTM(!rSpawner.m_spawnRequest.empty(), "spawnRequest Use_ shouldn't be triggered if rSpawner.m_spawnRequest is empty!"); + //LGRN_ASSERTM(!rSpawner.m_spawnRequest.empty(), "spawnRequest Use_ shouldn't run if rSpawner.m_spawnRequest is empty!"); rSpawner.m_ents.resize(rSpawner.m_spawnRequest.size() * 2); rBasic.m_activeIds.create(rSpawner.m_ents.begin(), rSpawner.m_ents.end()); @@ -140,7 +145,7 @@ Session setup_shape_spawn( rBuilder.task() .name ("Add mesh and material to spawned shapes") .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New), tgCS.drawEntResized(ModifyOrSignal)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idSpawner, idNMesh }) .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept @@ -214,8 +219,7 @@ Session setup_shape_spawn( rBuilder.task() .name ("Clear Shape Spawning vector after use") - .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnRequest(Clear)}) + .run_on ({tgShSp.spawnRequest(Clear)}) .push_to (out.m_tasks) .args ({ idSpawner }) .func([] (ACtxShapeSpawner& rSpawner) noexcept diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index 26763c5c..fa8fffb7 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -28,7 +28,7 @@ #include "identifiers.h" #include "CameraController.h" -#include "../ActiveApplication.h" +#include "../MagnumApplication.h" #include #include @@ -66,11 +66,17 @@ namespace testapp::scenes Session setup_window_app( TopTaskBuilder& rBuilder, - ArrayView const topData) + ArrayView const topData, + Session const& application) { + auto const tgApp = application .get_pipelines< PlApplication >(); + Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_WINDOW_APP); - out.create_pipelines(rBuilder); + auto const tgWin = out.create_pipelines(rBuilder); + + rBuilder.pipeline(tgWin.inputs).parent(tgApp.mainLoop);//.wait_for_signal(Signal); + auto &rUserInput = osp::top_emplace(topData, idUserInput, 12); config_controls(rUserInput); @@ -83,19 +89,28 @@ Session setup_magnum( ArrayView const topData, Session const& windowApp, Session const& application, - ActiveApplication::Arguments args) + MagnumApplication::Arguments args) { OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); + auto const tgApp = application .get_pipelines< PlApplication >(); + auto& rUserInput = top_get(topData, idUserInput); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_MAGNUM); auto const tgMgn = out.create_pipelines(rBuilder); - out.m_cleanup = tgMgn.cleanup.tpl(Run_); + out.m_cleanup = tgMgn.cleanup; + + rBuilder.pipeline(tgMgn.sync).parent(tgApp.mainLoop).wait_for_signal(ModifyOrSignal); - // Order-dependent; ActiveApplication construction starts OpenGL context, needed by RenderGL - /* unused */ top_emplace(topData, idActiveApp, args, rUserInput); + rBuilder.pipeline(tgMgn.meshGL) .parent(tgMgn.sync); + rBuilder.pipeline(tgMgn.textureGL) .parent(tgMgn.sync); + rBuilder.pipeline(tgMgn.entMeshGL) .parent(tgMgn.sync); + rBuilder.pipeline(tgMgn.entTextureGL) .parent(tgMgn.sync); + + // Order-dependent; MagnumApplication construction starts OpenGL context, needed by RenderGL + /* unused */ top_emplace(topData, idActiveApp, args, rUserInput); auto &rRenderGl = top_emplace (topData, idRenderGl); SysRenderGL::setup_context(rRenderGl); @@ -128,15 +143,27 @@ Session setup_scene_renderer( OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); - auto const tgWin = windowApp .get_pipelines(); - auto const tgScn = scene .get_pipelines(); - auto const tgCS = commonScene .get_pipelines(); - auto const tgMgn = magnum .get_pipelines(); + auto const tgApp = application .get_pipelines< PlApplication >(); + auto const tgWin = windowApp .get_pipelines< PlWindowApp >(); + auto const tgScn = scene .get_pipelines< PlScene >(); + auto const tgCS = commonScene .get_pipelines< PlCommonScene >(); + auto const tgMgn = magnum .get_pipelines< PlMagnum >(); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_COMMON_RENDERER); auto const tgSR = out.create_pipelines(rBuilder); + rBuilder.pipeline(tgSR.render).parent(tgApp.mainLoop).wait_for_signal(ModifyOrSignal); + + rBuilder.pipeline(tgSR.fbo) .parent(tgSR.render); + rBuilder.pipeline(tgSR.scnRender) .parent(tgSR.render); + rBuilder.pipeline(tgSR.group) .parent(tgMgn.sync); + rBuilder.pipeline(tgSR.groupEnts) .parent(tgMgn.sync); + rBuilder.pipeline(tgSR.drawTransforms) .parent(tgMgn.sync); + rBuilder.pipeline(tgSR.camera) .parent(tgSR.render); + rBuilder.pipeline(tgSR.entMesh) .parent(tgMgn.sync); + rBuilder.pipeline(tgSR.entTexture) .parent(tgMgn.sync); + top_emplace< ACtxSceneRenderGL > (topData, idScnRender); top_emplace< RenderGroup > (topData, idGroupFwd); @@ -182,10 +209,21 @@ Session setup_scene_renderer( SysRenderGL::compile_resource_textures(rDrawingRes, rResources, rRenderGl); }); + rBuilder.task() + .name ("Schedule Assign GL textures") + .schedules ({tgCS.entTextureDirty(Schedule_)}) + .sync_with ({tgCS.texture(Ready)}) + .push_to (out.m_tasks) + .args ({ idDrawing }) + .func([] (ACtxDrawing& rDrawing) noexcept -> TaskActions + { + return rDrawing.m_diffuseDirty.empty() ? TaskAction::Cancel : TaskActions{}; + }); + rBuilder.task() .name ("Assign GL textures to entities with scene textures") .run_on ({tgCS.entTextureDirty(UseOrRun)}) - .sync_with ({tgCS.texture(Ready), tgMgn.textureGL(Ready), tgMgn.entTextureGL(Modify)}) + .sync_with ({tgCS.texture(Ready), tgMgn.textureGL(Ready), tgMgn.entTextureGL(Modify), tgCS.drawEntResized(Done)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept @@ -193,10 +231,21 @@ Session setup_scene_renderer( SysRenderGL::assign_textures(rDrawing.m_diffuseTex, rDrawingRes.m_texToRes, rDrawing.m_diffuseDirty, rScnRender.m_diffuseTexId, rRenderGl); }); + rBuilder.task() + .name ("Schedule Assign GL meshes") + .schedules ({tgCS.entMeshDirty(Schedule_)}) + .sync_with ({tgCS.mesh(Ready)}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) + .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept -> TaskActions + { + return rDrawing.m_meshDirty.empty() ? TaskAction::Cancel : TaskActions{}; + }); + rBuilder.task() .name ("Assign GL meshes to entities with scene meshes") .run_on ({tgCS.entMeshDirty(UseOrRun)}) - .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(Ready), tgMgn.entMeshGL(Modify)}) + .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(Ready), tgMgn.entMeshGL(Modify), tgCS.drawEntResized(Done)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept diff --git a/src/test_application/activescenes/scene_renderer.h b/src/test_application/activescenes/scene_renderer.h index 02fb5700..bb65440f 100644 --- a/src/test_application/activescenes/scene_renderer.h +++ b/src/test_application/activescenes/scene_renderer.h @@ -28,7 +28,7 @@ #include -#include "../ActiveApplication.h" +#include "../MagnumApplication.h" namespace testapp::scenes @@ -36,14 +36,15 @@ namespace testapp::scenes osp::Session setup_window_app( osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData); + osp::ArrayView topData, + osp::Session const& application); osp::Session setup_magnum( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, osp::Session const& windowApp, osp::Session const& application, - ActiveApplication::Arguments args); + MagnumApplication::Arguments args); /** * @brief Magnum-powered OpenGL Renderer diff --git a/src/test_application/execution.cpp b/src/test_application/execution.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test_application/execution.h b/src/test_application/execution.h deleted file mode 100644 index cbff0549..00000000 --- a/src/test_application/execution.h +++ /dev/null @@ -1,62 +0,0 @@ -#include - -namespace testapp -{ - - - - - -} // namespace testapp -/** - * @brief Close sessions, delete all their associated TopData, Tasks, and Targets. - */ -void top_close_session(Tasks& rTasks, TaskGraph const& graph, TopTaskDataVec_t& rTaskData, ArrayView topData, ExecContext& rExec, ArrayView sessions); - - - -void top_close_session( - Tasks & rTasks, - TaskGraph const& graph, - TopTaskDataVec_t& rTaskData, - ArrayView topData, - ExecContext& rExec, - ArrayView sessions) -{ - // Run cleanup pipelines - for (Session &rSession : sessions) - { - if (rSession.m_cleanup.pipeline != lgrn::id_null()) - { - exec_request_run(rExec, rSession.m_cleanup.pipeline); - } - } - exec_update(rTasks, graph, rExec); - top_run_blocking(rTasks, graph, rTaskData, topData, rExec); - - // Clear each session's TopData - for (Session &rSession : sessions) - { - for (TopDataId const id : std::exchange(rSession.m_data, {})) - { - if (id != lgrn::id_null()) - { - topData[std::size_t(id)].reset(); - } - } - } - - // Clear each session's tasks - for (Session &rSession : sessions) - { - for (TaskId const task : rSession.m_tasks) - { - rTasks.m_taskIds.remove(task); - - TopTask &rCurrTaskData = rTaskData[task]; - rCurrTaskData.m_debugName.clear(); - rCurrTaskData.m_dataUsed.clear(); - rCurrTaskData.m_func = nullptr; - } - } -} diff --git a/src/test_application/executor.cpp b/src/test_application/executor.cpp new file mode 100644 index 00000000..09ae3128 --- /dev/null +++ b/src/test_application/executor.cpp @@ -0,0 +1,76 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * 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 "executor.h" + +#include + +#include + +namespace testapp +{ + +void SingleThreadedExecutor::load(TestAppTasks& rAppTasks) +{ + osp::exec_conform(rAppTasks.m_tasks, m_execContext); + m_execContext.doLogging = m_log != nullptr; +} + +void SingleThreadedExecutor::run(TestAppTasks& rAppTasks, osp::PipelineId pipeline) +{ + osp::exec_request_run(m_execContext, pipeline); +} + +void SingleThreadedExecutor::signal(TestAppTasks& rAppTasks, osp::PipelineId pipeline) +{ + osp::exec_signal(m_execContext, pipeline); +} + +void SingleThreadedExecutor::wait(TestAppTasks& rAppTasks) +{ + if (m_log != nullptr) + { + m_log->info("\n>>>>>>>>>> Previous State Changes\n{}\n>>>>>>>>>> Current State\n{}\n", + osp::TopExecWriteLog {rAppTasks.m_tasks, rAppTasks.m_taskData, rAppTasks.m_graph, m_execContext}, + osp::TopExecWriteState{rAppTasks.m_tasks, rAppTasks.m_taskData, rAppTasks.m_graph, m_execContext} ); + m_execContext.logMsg.clear(); + } + + osp::exec_update(rAppTasks.m_tasks, rAppTasks.m_graph, m_execContext); + osp::top_run_blocking(rAppTasks.m_tasks, rAppTasks.m_graph, rAppTasks.m_taskData, rAppTasks.m_topData, m_execContext); + + if (m_log != nullptr) + { + m_log->info("\n>>>>>>>>>> New State Changes\n{}", + osp::TopExecWriteLog{rAppTasks.m_tasks, rAppTasks.m_taskData, rAppTasks.m_graph, m_execContext} ); + m_execContext.logMsg.clear(); + } +} + +bool SingleThreadedExecutor::is_running(TestAppTasks const& appTasks) +{ + return m_execContext.hasRequestRun || (m_execContext.pipelinesRunning != 0); +} + +} // namespace testapp diff --git a/src/osp/tasks/top_session.cpp b/src/test_application/executor.h similarity index 63% rename from src/osp/tasks/top_session.cpp rename to src/test_application/executor.h index 7a98e976..9099b50d 100644 --- a/src/osp/tasks/top_session.cpp +++ b/src/test_application/executor.h @@ -1,6 +1,6 @@ /** * Open Space Program - * Copyright © 2019-2022 Open Space Program Project + * Copyright © 2019-2023 Open Space Program Project * * MIT License * @@ -22,17 +22,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "top_session.h" -#include "tasks.h" +#pragma once -#include +#include "testapp.h" -#include +#include -#include +#include -namespace osp +namespace testapp { +struct SingleThreadedExecutor final : public IExecutor +{ + void load(TestAppTasks& rAppTasks) override; + + void run(TestAppTasks& rAppTasks, osp::PipelineId pipeline) override; + + void signal(TestAppTasks& rAppTasks, osp::PipelineId pipeline) override; + + void wait(TestAppTasks& rAppTasks) override; + + bool is_running(TestAppTasks const& rAppTasks) override; + + osp::ExecContext m_execContext; + + std::shared_ptr m_log; +}; + +} // namespace testapp -} // namespace osp diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index 10ca24d7..04206ef4 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -22,11 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "MagnumApplication.h" +#include "executor.h" #include "testapp.h" -#include - -#include "ActiveApplication.h" #include "activescenes/scenarios.h" #include "activescenes/identifiers.h" #include "activescenes/scene_renderer.h" @@ -56,6 +55,8 @@ #include +#include + #include #include #include @@ -75,7 +76,7 @@ using namespace testapp; void debug_cli_loop(); /** - * @brief Starts Magnum application (ActiveApplication) thread g_magnumThread + * @brief Starts Magnum application (MagnumApplication) thread g_magnumThread * * This initializes an OpenGL context, and opens the window */ @@ -97,10 +98,13 @@ void debug_print_resources(); TestApp g_testApp; +SingleThreadedExecutor g_executor; + std::thread g_magnumThread; // Loggers std::shared_ptr g_logTestApp; +std::shared_ptr g_logExecutor; std::shared_ptr g_logMagnumApp; // lazily save the arguments to pass to Magnum @@ -112,10 +116,11 @@ int main(int argc, char** argv) { Corrade::Utility::Arguments args; args.addSkippedPrefix("magnum", "Magnum options") - .addOption("scene", "none") .setHelp("scene", "Set the scene to launch") - .addOption("config") .setHelp("config", "path to configuration file to use") - .addBooleanOption("norepl") .setHelp("norepl", "don't enter read, evaluate, print, loop.") - .addBooleanOption('v', "verbose") .setHelp("verbose", "log verbosely") + .addOption("scene", "none") .setHelp("scene", "Set the scene to launch") + .addOption("config") .setHelp("config", "path to configuration file to use") + .addBooleanOption("norepl") .setHelp("norepl", "don't enter read, evaluate, print, loop.") + .addBooleanOption("log-exec") .setHelp("log-exec", "Log Task/Pipeline Execution (Extremely chatty!)") + // TODO .addBooleanOption('v', "verbose") .setHelp("verbose", "log verbosely") .setGlobalHelp("Helptext goes here.") .parse(argc, argv); @@ -127,11 +132,20 @@ int main(int argc, char** argv) { auto pSink = std::make_shared(); pSink->set_pattern("[%T.%e] [%n] [%^%l%$] [%s:%#] %v"); - g_logTestApp = std::make_shared("testapp", pSink); + g_logTestApp = std::make_shared("testapp", pSink); + g_logExecutor = std::make_shared("executor", pSink); g_logMagnumApp = std::make_shared("flight", std::move(pSink)); } - osp::set_thread_logger(g_logTestApp); // Set logger for this thread + // Set thread-local logger used by OSP_LOG_* macros + osp::set_thread_logger(g_logTestApp); + + g_testApp.m_pExecutor = &g_executor; + + if (args.isSet("log-exec")) + { + g_executor.m_log = g_logExecutor; + } g_testApp.m_topData.resize(64); load_a_bunch_of_stuff(); @@ -142,7 +156,7 @@ int main(int argc, char** argv) if(it == std::end(scenarios())) { std::cerr << "unknown scene" << std::endl; - clear_resource_owners(g_testApp); + g_testApp.clear_resource_owners(); exit(-1); } @@ -195,7 +209,7 @@ void debug_cli_loop() { std::cout << "Loading scene: " << it->first << "\n"; - close_sessions(g_testApp, g_testApp.m_scene); // Close existing scene + //g_testApp.close_sessions(g_executor, g_testApp.m_scene); // Close existing scene g_testApp.m_rendererSetup = it->second.m_setup(g_testApp); start_magnum_async(); @@ -231,7 +245,7 @@ void debug_cli_loop() { // Request exit if application exists OSP_DECLARE_GET_DATA_IDS(g_testApp.m_renderer.m_sessions[1], TESTAPP_DATA_MAGNUM); // declares idActiveApp - osp::top_get(g_testApp.m_topData, idActiveApp).exit(); + osp::top_get(g_testApp.m_topData, idActiveApp).exit(); } break; @@ -242,7 +256,7 @@ void debug_cli_loop() } } - clear_resource_owners(g_testApp); + g_testApp.clear_resource_owners(); } void start_magnum_async() @@ -258,35 +272,34 @@ void start_magnum_async() // Start Magnum application session osp::TopTaskBuilder builder{g_testApp.m_tasks, g_testApp.m_renderer.m_edges, g_testApp.m_taskData}; - g_testApp.m_windowApp = scenes::setup_window_app (builder, g_testApp.m_topData); + g_testApp.m_windowApp = scenes::setup_window_app (builder, g_testApp.m_topData, g_testApp.m_application); g_testApp.m_magnum = scenes::setup_magnum (builder, g_testApp.m_topData, g_testApp.m_windowApp, g_testApp.m_application, {g_argc, g_argv}); OSP_DECLARE_GET_DATA_IDS(g_testApp.m_magnum, TESTAPP_DATA_MAGNUM); // declares idActiveApp - auto &rActiveApp = osp::top_get(g_testApp.m_topData, idActiveApp); + auto &rActiveApp = osp::top_get(g_testApp.m_topData, idActiveApp); // Setup renderer sessions g_testApp.m_rendererSetup(g_testApp); - //g_testApp.m_graph.reset(); - //g_testApp.m_graph.emplace(osp::make_exec_graph(g_testApp.m_tasks, {&g_testApp.m_renderer.m_edges, &g_testApp.m_scene.m_edges})); - //osp::exec_conform(g_testApp.m_tasks, g_testApp.m_exec); - - //exec_update(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_exec); - //top_run_blocking(g_testApp.m_tasks, g_testApp.m_graph.value(), g_testApp.m_taskData, g_testApp.m_topData, g_testApp.m_exec); + g_testApp.m_graph = osp::make_exec_graph(g_testApp.m_tasks, {&g_testApp.m_renderer.m_edges, &g_testApp.m_scene.m_edges}); + g_executor.load(g_testApp); // Starts the main loop. This function is blocking, and will only return - // once the window is closed. See ActiveApplication::drawEvent + // once the window is closed. See MagnumApplication::drawEvent rActiveApp.exec(); // Destruct draw function lambda first // EngineTest stores the entire renderer in here (if it's active) - rActiveApp.set_on_draw({}); + rActiveApp.set_osp_app({}); // Closing sessions will delete their associated TopData and Tags - close_sessions(g_testApp, g_testApp.m_renderer); - close_session(g_testApp, g_testApp.m_magnum); - close_session(g_testApp, g_testApp.m_windowApp); + g_testApp.close_sessions(g_testApp.m_renderer.m_sessions); + g_testApp.m_renderer.m_sessions.clear(); + g_testApp.m_renderer.m_edges.m_syncWith.clear(); + + g_testApp.close_session(g_testApp.m_magnum); + g_testApp.close_session(g_testApp.m_windowApp); OSP_LOG_INFO("Closed Magnum Application"); }); @@ -305,11 +318,37 @@ void load_a_bunch_of_stuff() std::size_t const maxTags = 256; // aka: just two 64-bit integers std::size_t const maxTagsInts = maxTags / 64; - // declares idResources - //g_testApp.m_application.create_pipelines<> + osp::TopTaskBuilder builder{g_testApp.m_tasks, g_testApp.m_applicationGroup.m_edges, g_testApp.m_taskData}; + auto const plApp = g_testApp.m_application.create_pipelines(builder); + + builder.pipeline(plApp.mainLoop).loops(true).wait_for_signal(EStgOptn::ModifyOrSignal); + + // declares idResources and idMainLoopCtrl OSP_DECLARE_CREATE_DATA_IDS(g_testApp.m_application, g_testApp.m_topData, TESTAPP_DATA_APPLICATION); - auto &rResources = osp::top_emplace(g_testApp.m_topData, idResources); + auto &rResources = osp::top_emplace (g_testApp.m_topData, idResources); + /* unused */ osp::top_emplace(g_testApp.m_topData, idMainLoopCtrl); + + builder.task() + .name ("Schedule Main Loop") + .schedules ({plApp.mainLoop(EStgOptn::Schedule)}) + .push_to (g_testApp.m_application.m_tasks) + .args ({ idMainLoopCtrl}) + .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions + { + if ( ! rMainLoopCtrl.doUpdate + && ! rMainLoopCtrl.doSync + && ! rMainLoopCtrl.doResync + && ! rMainLoopCtrl.doRender) + { + return osp::TaskAction::Cancel; + } + else + { + return { }; + } + }); + rResources.resize_types(osp::ResTypeIdReg_t::size()); diff --git a/src/test_application/testapp.cpp b/src/test_application/testapp.cpp index ce39ef0e..1edb7dfd 100644 --- a/src/test_application/testapp.cpp +++ b/src/test_application/testapp.cpp @@ -34,27 +34,58 @@ namespace testapp { -void close_sessions(TestAppTasks &rTestApp, osp::SessionGroup &rSessions) +void TestApp::close_sessions(osp::ArrayView const sessions) { -// rSessions.m_edges.m_syncWith .clear(); + using namespace osp; -// if ( rSessions.m_sessions.empty() || ! rTestApp.m_graph.has_value() ) -// { -// return; -// } + // Run cleanup pipelines + for (Session &rSession : sessions) + { + if (rSession.m_cleanup != lgrn::id_null()) + { + m_pExecutor->run(*this, rSession.m_cleanup); + } + } + m_pExecutor->wait(*this); + + // Clear each session's TopData + for (Session &rSession : sessions) + { + for (TopDataId const id : std::exchange(rSession.m_data, {})) + { + if (id != lgrn::id_null()) + { + m_topData[std::size_t(id)].reset(); + } + } + } -// TestApp testapp; -// testapp.m_topData.clear(); + // Clear each session's tasks and pipelines + for (Session &rSession : sessions) + { + for (TaskId const task : rSession.m_tasks) + { + m_tasks.m_taskIds.remove(task); -// osp::top_close_session(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec, rSessions.m_sessions); + TopTask &rCurrTaskData = m_taskData[task]; + rCurrTaskData.m_debugName.clear(); + rCurrTaskData.m_dataUsed.clear(); + rCurrTaskData.m_func = nullptr; + } + rSession.m_tasks.clear(); -// rSessions.m_sessions.clear(); + for (PipelineId const pipeline : rSession.m_pipelines) + { + m_tasks.m_pipelineIds.remove(pipeline); + } + rSession.m_pipelines.clear(); + } } -void close_session(TestAppTasks &rTestApp, osp::Session &rSession) +void TestApp::close_session(osp::Session &rSession) { - //osp::top_close_session(rTestApp.m_tasks, rTestApp.m_graph.value(), rTestApp.m_taskData, rTestApp.m_topData, rTestApp.m_exec, osp::ArrayView(&rSession, 1)); + close_sessions(osp::ArrayView(&rSession, 1)); } @@ -71,14 +102,15 @@ static void resource_for_each_type(osp::ResTypeId const type, osp::Resources& rR } } -void clear_resource_owners(TestApp& rTestApp) + +void TestApp::clear_resource_owners() { using namespace osp::restypes; // declares idResources - OSP_DECLARE_CREATE_DATA_IDS(rTestApp.m_application, rTestApp.m_topData, TESTAPP_DATA_APPLICATION); + OSP_DECLARE_CREATE_DATA_IDS(m_application, m_topData, TESTAPP_DATA_APPLICATION); - auto &rResources = osp::top_get(rTestApp.m_topData, idResources); + auto &rResources = osp::top_get(m_topData, idResources); // Texture resources contain osp::TextureImgSource, which refererence counts // their associated image data diff --git a/src/test_application/testapp.h b/src/test_application/testapp.h index 8971c0b5..794ce1d5 100644 --- a/src/test_application/testapp.h +++ b/src/test_application/testapp.h @@ -41,11 +41,8 @@ namespace testapp struct TestApp; using RendererSetupFunc_t = void(*)(TestApp&); - using SceneSetupFunc_t = RendererSetupFunc_t(*)(TestApp&); - - struct TestAppTasks { std::vector m_topData; @@ -54,17 +51,31 @@ struct TestAppTasks osp::TaskGraph m_graph; }; -class IExecutor +struct IExecutor { - virtual void load(TestAppTasks const* pTasks) = 0; - virtual void run(osp::PipelineId pipeline) = 0; - virtual void signal(osp::PipelineId pipeline) = 0; - virtual void wait() = 0; - virtual bool is_done() = 0; + virtual void load(TestAppTasks& rAppTasks) = 0; + + virtual void run(TestAppTasks& rAppTasks, osp::PipelineId pipeline) = 0; + + virtual void signal(TestAppTasks& rAppTasks, osp::PipelineId pipeline) = 0; + + virtual void wait(TestAppTasks& rAppTasks) = 0; + + virtual bool is_running(TestAppTasks const& rAppTasks) = 0; }; struct TestApp : TestAppTasks { + void close_sessions(osp::ArrayView sessions); + + void close_session(osp::Session &rSession); + + /** + * @brief Deal with resource reference counts for a clean termination + */ + void clear_resource_owners(); + + osp::SessionGroup m_applicationGroup; osp::Session m_application; osp::SessionGroup m_scene; @@ -73,21 +84,12 @@ struct TestApp : TestAppTasks osp::Session m_magnum; osp::SessionGroup m_renderer; - IExecutor* m_executor{ nullptr }; - RendererSetupFunc_t m_rendererSetup { nullptr }; - //osp::TopDataId m_idResources { lgrn::id_null() }; + IExecutor *m_pExecutor { nullptr }; + osp::PkgId m_defaultPkg { lgrn::id_null() }; }; -void close_sessions(TestAppTasks &rTestApp, osp::SessionGroup &rSessions); - -void close_session(TestAppTasks &rTestApp, osp::Session &rSession); - -/** - * @brief Deal with resource reference counts for a clean termination - */ -void clear_resource_owners(TestApp& rTestApp); } // namespace testapp diff --git a/test/tasks/main.cpp b/test/tasks/main.cpp index 8d77ad90..4d4328db 100644 --- a/test/tasks/main.cpp +++ b/test/tasks/main.cpp @@ -483,7 +483,7 @@ struct TestState int checks { 0 }; }; -enum class Stages { Schedule, Process, Done, Clear }; +enum class Stages { Signal, Schedule, Process, Done, Clear }; struct Pipelines { @@ -514,7 +514,7 @@ TEST(Tasks, BasicSingleThreadedNestedLoop) auto const pl = builder.create_pipelines(); - builder.pipeline(pl.loopOuter).loops(true).wait_for_signal(Schedule); + builder.pipeline(pl.loopOuter).loops(true).wait_for_signal(Signal); builder.pipeline(pl.loopInner).loops(true).parent(pl.loopOuter); builder.task() From c101a2f22dda32a71791282fad51a19f208e0f69 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sat, 26 Aug 2023 16:21:02 -0700 Subject: [PATCH 32/35] Fix more pipeline stuff --- src/osp/tasks/execute.cpp | 18 +++++++--- .../activescenes/identifiers.h | 5 +-- .../activescenes/scenarios.cpp | 20 ++++++----- .../activescenes/scene_common.cpp | 22 ++++++------ .../activescenes/scene_newton.cpp | 34 +++++++++++-------- .../activescenes/scene_physics.cpp | 9 ++--- .../activescenes/scene_renderer.cpp | 29 ++++++++-------- 7 files changed, 77 insertions(+), 60 deletions(-) diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 47ff9389..768bc679 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -311,14 +311,17 @@ static void loop_scope_done(Tasks const& tasks, TaskGraph const& graph, ExecCont { // Loop finished + LGRN_ASSERT(rExecPl.loopChildrenLeft == 0); + subtree_for_each( - {.root = treePos, .includeRoot = true}, graph, rExec, - [&tasks, &graph, &rExec] - (PipelineTreePos_t const loopPos, PipelineId const loopPipeline, uint32_t const descendants) + {.root = treePos, .includeRoot = true}, graph, rExec, + [&tasks, &graph, &rExec] + (PipelineTreePos_t const loopPos, PipelineId const loopPipeline, uint32_t const descendants) { ExecPipeline &rLoopExecPl = rExec.plData[loopPipeline]; LGRN_ASSERT(rLoopExecPl.running == true); + LGRN_ASSERT(rLoopExecPl.stage == lgrn::id_null()); rLoopExecPl.running = false; rLoopExecPl.canceled = false; @@ -349,7 +352,7 @@ static void loop_scope_done(Tasks const& tasks, TaskGraph const& graph, ExecCont LGRN_ASSERT(rParentScopeExecPl.loopChildrenLeft != 0); -- rParentScopeExecPl.loopChildrenLeft; - if (rExecPl.loopChildrenLeft == 0) + if (rParentScopeExecPl.loopChildrenLeft) { loop_scope_done(tasks, graph, rExec, rParentScopeExecPl, parentScopePl, parentScopeTreePos); } @@ -740,6 +743,13 @@ static void pipeline_cancel(Tasks const& tasks, TaskGraph const& graph, ExecCont cancel_stage_ahead(AnyStageId(anystgInt)); } + // In this case, "stage == null" means the pipeline is done; advancing it would + // accidentally restart the pipeline. + if (rCancelExecPl.stage != lgrn::id_null()) + { + pipeline_try_advance(rExec, rCancelExecPl, cancelPl); + } + exec_log(rExec, ExecContext::PipelineCancel{cancelPl, rCancelExecPl.stage}); } }); diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index f0a46393..a1f1b1ee 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -152,7 +152,8 @@ struct PlCommonScene idPhys, idHierBody, idPhysIn struct PlPhysics { - PipelineDef physics {"physics"}; + PipelineDef physBody {"physBody"}; + PipelineDef physUpdate {"physUpdate"}; }; @@ -243,7 +244,7 @@ struct PlShapeSpawn idNwt struct PlNewton { - PipelineDef nwtBody; + PipelineDef nwtBody {"nwtBody"}; }; #define TESTAPP_DATA_NEWTON_FORCES 1, \ diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index b408e136..ca8bed31 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -63,10 +63,11 @@ static constexpr int sc_materialCount = 4; struct CommonMagnumApp : IOspApplication { - CommonMagnumApp(TestApp &rTestApp, MainLoopControl &rMainLoopCtrl, PipelineId mainLoop, PipelineId renderSync, PipelineId sceneUpdate, PipelineId sceneRender) noexcept + CommonMagnumApp(TestApp &rTestApp, MainLoopControl &rMainLoopCtrl, PipelineId mainLoop, PipelineId inputs, PipelineId renderSync, PipelineId sceneUpdate, PipelineId sceneRender) noexcept : m_rTestApp { rTestApp } , m_rMainLoopCtrl { rMainLoopCtrl } , m_mainLoop { mainLoop } + , m_inputs { inputs } , m_renderSync { renderSync } , m_sceneUpdate { sceneUpdate } , m_sceneRender { sceneRender } @@ -93,6 +94,7 @@ struct CommonMagnumApp : IOspApplication }; m_rTestApp.m_pExecutor->signal(m_rTestApp, m_mainLoop); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_inputs); m_rTestApp.m_pExecutor->signal(m_rTestApp, m_sceneUpdate); m_rTestApp.m_pExecutor->signal(m_rTestApp, m_sceneRender); m_rTestApp.m_pExecutor->signal(m_rTestApp, m_renderSync); @@ -110,6 +112,7 @@ struct CommonMagnumApp : IOspApplication }; m_rTestApp.m_pExecutor->signal(m_rTestApp, m_mainLoop); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_inputs); m_rTestApp.m_pExecutor->signal(m_rTestApp, m_sceneUpdate); m_rTestApp.m_pExecutor->signal(m_rTestApp, m_sceneRender); m_rTestApp.m_pExecutor->signal(m_rTestApp, m_renderSync); @@ -119,6 +122,7 @@ struct CommonMagnumApp : IOspApplication if (m_rTestApp.m_pExecutor->is_running(m_rTestApp)) { // Main loop must have stopped, but didn't! + m_rTestApp.m_pExecutor->wait(m_rTestApp); std::abort(); } } @@ -127,6 +131,7 @@ struct CommonMagnumApp : IOspApplication MainLoopControl &m_rMainLoopCtrl; PipelineId m_mainLoop; + PipelineId m_inputs; PipelineId m_renderSync; PipelineId m_sceneUpdate; PipelineId m_sceneRender; @@ -145,11 +150,12 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); PipelineId const mainLoop = rTestApp.m_application .get_pipelines() .mainLoop; + PipelineId const inputs = rTestApp.m_windowApp .get_pipelines() .inputs; PipelineId const renderSync = rTestApp.m_magnum .get_pipelines() .sync; PipelineId const sceneUpdate = scene .get_pipelines() .update; PipelineId const sceneRender = scnRenderer .get_pipelines() .render; - rActiveApp.set_osp_app( std::make_unique(rTestApp, rMainLoopCtrl, mainLoop, renderSync, sceneUpdate, sceneRender) ); + rActiveApp.set_osp_app( std::make_unique(rTestApp, rMainLoopCtrl, mainLoop, inputs, renderSync, sceneUpdate, sceneRender) ); } template @@ -230,10 +236,10 @@ static ScenarioMap_t make_scenarios() //droppers = setup_droppers (builder, rTopData, commonScene, shapeSpawn); //bounds = setup_bounds (builder, rTopData, commonScene, physics, shapeSpawn); - //newton = setup_newton (builder, rTopData, scene, commonScene, physics); - //nwtGravSet = setup_newton_factors (builder, rTopData); + newton = setup_newton (builder, rTopData, scene, commonScene, physics); + nwtGravSet = setup_newton_factors (builder, rTopData); //nwtGrav = setup_newton_force_accel (builder, rTopData, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); - //shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, commonScene, physics, shapeSpawn, newton, nwtGravSet); + shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, commonScene, physics, shapeSpawn, newton, nwtGravSet); create_materials(rTopData, commonScene, sc_materialCount); add_floor(rTopData, application, commonScene, shapeSpawn, sc_matVisualizer, defaultPkg); @@ -242,10 +248,6 @@ static ScenarioMap_t make_scenarios() // auto const tgCS = commonScene .get_pipelines(); // auto const tgShSp = shapeSpawn .get_pipelines(); - - - //exec_conform(rTestApp.m_tasks, rTestApp.m_exec); - return [] (TestApp& rTestApp) { auto const application = rTestApp.m_application; diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index 764a0037..543e59f5 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -58,15 +58,15 @@ Session setup_scene( rBuilder.pipeline(plScn.update).parent(tgApp.mainLoop).wait_for_signal(ModifyOrSignal); - rBuilder.task() - .name ("Schedule Scene update") - .schedules ({plScn.update(Schedule)}) - .push_to (out.m_tasks) - .args ({ idMainLoopCtrl}) - .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions - { - return rMainLoopCtrl.doUpdate ? osp::TaskActions{} : osp::TaskAction::Cancel; - }); +// rBuilder.task() +// .name ("Schedule Scene update") +// .schedules ({plScn.update(Schedule)}) +// .push_to (out.m_tasks) +// .args ({ idMainLoopCtrl}) +// .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions +// { +// return rMainLoopCtrl.doUpdate ? osp::TaskActions{} : osp::TaskAction::Cancel; +// }); return out; } @@ -168,8 +168,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete drawing components") - .run_on ({tgCS.drawEntDelete(UseOrRun)}) - .sync_with ({tgCS.mesh(Delete), tgCS.texture(Delete)}) + .run_on ({tgCS.mesh(Delete)}) + .sync_with ({tgCS.texture(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept diff --git a/src/test_application/activescenes/scene_newton.cpp b/src/test_application/activescenes/scene_newton.cpp index 43c96add..773bcde6 100644 --- a/src/test_application/activescenes/scene_newton.cpp +++ b/src/test_application/activescenes/scene_newton.cpp @@ -56,7 +56,6 @@ using Corrade::Containers::arrayView; namespace testapp::scenes { -#if 0 Session setup_newton( TopTaskBuilder& rBuilder, @@ -69,13 +68,15 @@ Session setup_newton( OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); - auto const tgScn = scene .get_targets(); - auto const tgCS = commonScene .get_targets(); - auto const tgPhy = physics .get_targets(); + auto const tgScn = scene .get_pipelines(); + auto const tgCS = commonScene .get_pipelines(); + auto const tgPhy = physics .get_pipelines(); Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_NEWTON); - auto const tgNwt = out.create_targets(rBuilder); + auto const tgNwt = out.create_pipelines(rBuilder); + + rBuilder.pipeline(tgNwt.nwtBody).parent(tgScn.update); top_emplace< ACtxNwtWorld >(topData, idNwt, 2); @@ -83,8 +84,8 @@ Session setup_newton( rBuilder.task() .name ("Delete Newton components") - .trigger_on ({tgCS.delActiveEnt_mod}) - .fulfills ({tgCS.delActiveEnt_use, tgNwt.nwtBody_del, tgNwt.nwtBody_new}) + .run_on ({tgCS.activeEntDelete(UseOrRun)}) + .sync_with ({tgNwt.nwtBody(Delete)}) .push_to (out.m_tasks) .args({ idNwt, idActiveEntDel }) .func([] (ACtxNwtWorld& rNwt, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -94,9 +95,8 @@ Session setup_newton( rBuilder.task() .name ("Update Newton world") - .trigger_on ({tgScn.time}) - .depends_on ({tgNwt.nwtBody_mod, tgCS.hier_mod, tgPhy.physics_mod, tgCS.transform_new}) - .fulfills ({tgNwt.nwtBody_use, tgCS.hier_use, tgPhy.physics_use, tgCS.transform_mod}) + .run_on ({tgScn.update(Run)}) + .sync_with ({/*tgNwt.nwtBody(Modify),*/ tgCS.hierarchy(Modify), tgPhy.physUpdate(Run), tgCS.transform(Modify)}) .push_to (out.m_tasks) .args({ idBasic, idPhys, idNwt, idDeltaTimeIn }) .func([] (ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, float const deltaTimeIn, WorkerContext ctx) noexcept @@ -185,16 +185,17 @@ Session setup_shape_spawn_newton( OSP_DECLARE_GET_DATA_IDS(newton, TESTAPP_DATA_NEWTON); OSP_DECLARE_GET_DATA_IDS(nwtFactors, TESTAPP_DATA_NEWTON_FORCES); - auto const tgShSp = shapeSpawn .get_targets(); - auto const tgNwt = newton .get_targets(); + auto const tgPhy = physics .get_pipelines(); + + auto const tgShSp = shapeSpawn .get_pipelines(); + auto const tgNwt = newton .get_pipelines(); Session out; rBuilder.task() .name ("Add Newton physics to spawned shapes") - .trigger_on ({tgShSp.spawnRequest_mod}) - .depends_on ({tgShSp.spawnedEnts_mod}) - .fulfills ({tgShSp.spawnedEnts_use, tgNwt.nwtBody_new, tgNwt.nwtBody_mod}) + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgNwt.nwtBody(New), tgPhy.physUpdate(Done)}) .push_to (out.m_tasks) .args({ idBasic, idSpawner, idPhys, idNwt, idNwtFactors }) .func([] (ACtxBasic const &rBasic, ACtxShapeSpawner& rSpawner, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ForceFactors_t nwtFactors) noexcept @@ -232,6 +233,9 @@ Session setup_shape_spawn_newton( return out; } +#if 0 + + void compound_collect_recurse( ACtxPhysics const& rCtxPhys, ACtxNwtWorld& rCtxWorld, diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index 7e6d8343..ec861619 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -64,14 +64,15 @@ Session setup_physics( OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_PHYSICS); auto const tgPhy = out.create_pipelines(rBuilder); - rBuilder.pipeline(tgPhy.physics).parent(tgScn.update); + rBuilder.pipeline(tgPhy.physBody) .parent(tgScn.update); + rBuilder.pipeline(tgPhy.physUpdate).parent(tgScn.update); top_emplace< ACtxPhysics > (topData, idPhys); rBuilder.task() .name ("Delete Physics components") .run_on ({tgCS.activeEntDelete(UseOrRun)}) - .sync_with ({tgPhy.physics(Delete)}) + .sync_with ({tgPhy.physBody(Delete)}) .push_to (out.m_tasks) .args ({ idPhys, idActiveEntDel }) .func([] (ACtxPhysics& rPhys, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -145,7 +146,7 @@ Session setup_shape_spawn( rBuilder.task() .name ("Add mesh and material to spawned shapes") .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New), tgCS.drawEntResized(ModifyOrSignal)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New), tgCS.drawEntResized(ModifyOrSignal), tgCS.materialDirty(Modify_)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idSpawner, idNMesh }) .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept @@ -187,7 +188,7 @@ Session setup_shape_spawn( rBuilder.task() .name ("Add physics to spawned shapes") .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgPhy.physics(New)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgPhy.physBody(Modify), tgPhy.physUpdate(Done)}) .push_to (out.m_tasks) .args ({ idBasic, idSpawner, idPhys }) .func([] (ACtxBasic const& rBasic, ACtxShapeSpawner& rSpawner, ACtxPhysics& rPhys) noexcept diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index fa8fffb7..ec3cc094 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -75,8 +75,7 @@ Session setup_window_app( OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_WINDOW_APP); auto const tgWin = out.create_pipelines(rBuilder); - rBuilder.pipeline(tgWin.inputs).parent(tgApp.mainLoop);//.wait_for_signal(Signal); - + rBuilder.pipeline(tgWin.inputs).parent(tgApp.mainLoop).wait_for_signal(ModifyOrSignal); auto &rUserInput = osp::top_emplace(topData, idUserInput, 12); config_controls(rUserInput); @@ -190,7 +189,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Compile Resource Meshes to GL") .run_on ({tgCS.meshResDirty(UseOrRun)}) - .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(New)}) + .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(New), tgCS.entMeshDirty(Modify_)}) .push_to (out.m_tasks) .args ({ idDrawingRes, idResources, idRenderGl }) .func([] (ACtxDrawingRes const& rDrawingRes, osp::Resources& rResources, RenderGL& rRenderGl) noexcept @@ -231,16 +230,16 @@ Session setup_scene_renderer( SysRenderGL::assign_textures(rDrawing.m_diffuseTex, rDrawingRes.m_texToRes, rDrawing.m_diffuseDirty, rScnRender.m_diffuseTexId, rRenderGl); }); - rBuilder.task() - .name ("Schedule Assign GL meshes") - .schedules ({tgCS.entMeshDirty(Schedule_)}) - .sync_with ({tgCS.mesh(Ready)}) - .push_to (out.m_tasks) - .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) - .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept -> TaskActions - { - return rDrawing.m_meshDirty.empty() ? TaskAction::Cancel : TaskActions{}; - }); +// rBuilder.task() +// .name ("Schedule Assign GL meshes") +// .schedules ({tgCS.entMeshDirty(Schedule_)}) +// .sync_with ({tgCS.mesh(Ready)}) +// .push_to (out.m_tasks) +// .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) +// .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept -> TaskActions +// { +// return rDrawing.m_meshDirty.empty() ? TaskAction::Cancel : TaskActions{}; +// }); rBuilder.task() .name ("Assign GL meshes to entities with scene meshes") @@ -278,7 +277,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Calculate draw transforms") .run_on ({tgSR.render(Run)}) - .sync_with ({tgCS.hierarchy(Ready), tgCS.activeEnt(Ready), tgSR.drawTransforms(Modify_), tgCS.drawEnt(Ready)}) + .sync_with ({tgCS.hierarchy(Ready), tgCS.transform(Ready), tgCS.activeEnt(Ready), tgSR.drawTransforms(Modify_), tgCS.drawEnt(Ready), tgCS.drawEntResized(Done)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idScnRender }) .func([] (ACtxBasic const& rBasic, ACtxDrawing const& rDrawing, ACtxSceneRenderGL& rScnRender) noexcept @@ -367,7 +366,7 @@ Session setup_shader_visualizer( rBuilder.task() .name ("Sync MeshVisualizer shader entities") .run_on ({tgCS.materialDirty(UseOrRun)}) - .sync_with ({tgSR.groupEnts(Ready), tgSR.group(Modify)}) + .sync_with ({tgSR.groupEnts(Modify), tgSR.group(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idGroupFwd, idDrawShVisual}) .func([] (ACtxDrawing const& rDrawing, RenderGroup& rGroupFwd, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept From 9a0b5901106e3cba80d20cf241ad8ecc5bba73c8 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sat, 26 Aug 2023 22:04:58 -0700 Subject: [PATCH 33/35] Rework renderer 'resync' logic --- src/osp/Active/SysRender.cpp | 6 - src/osp/Active/SysRender.h | 7 - src/osp/Active/opengl/SysRenderGL.cpp | 146 +++++++++--------- src/osp/Active/opengl/SysRenderGL.h | 42 ++++- src/osp/Shaders/MeshVisualizer.cpp | 27 ++++ src/osp/Shaders/MeshVisualizer.h | 55 +++---- src/osp/tasks/execute.cpp | 2 + src/osp/tasks/top_execute.cpp | 20 +-- .../activescenes/identifiers.h | 3 +- .../activescenes/scenarios.cpp | 16 +- .../activescenes/scenarios_enginetest.cpp | 23 ++- .../activescenes/scene_common.cpp | 40 +++-- .../activescenes/scene_physics.cpp | 4 +- .../activescenes/scene_renderer.cpp | 123 ++++++++++++--- 14 files changed, 332 insertions(+), 182 deletions(-) diff --git a/src/osp/Active/SysRender.cpp b/src/osp/Active/SysRender.cpp index 3326b10d..86e9e8c6 100644 --- a/src/osp/Active/SysRender.cpp +++ b/src/osp/Active/SysRender.cpp @@ -119,12 +119,6 @@ void SysRender::set_dirty_all(ACtxDrawing &rCtxDrawing) } } -void SysRender::clear_dirty_all(ACtxDrawing& rCtxDrawing) -{ - rCtxDrawing.m_meshDirty.clear(); - rCtxDrawing.m_diffuseDirty.clear(); -} - void SysRender::update_draw_transforms_recurse( ACtxSceneGraph const& rScnGraph, diff --git a/src/osp/Active/SysRender.h b/src/osp/Active/SysRender.h index 25602331..c5e8007a 100644 --- a/src/osp/Active/SysRender.h +++ b/src/osp/Active/SysRender.h @@ -190,13 +190,6 @@ class SysRender */ static void set_dirty_all(ACtxDrawing& rCtxDrawing); - /** - * @brief Clear all dirty flags/vectors - * - * @param rCtxDrawing [ref] Drawing data - */ - static void clear_dirty_all(ACtxDrawing& rCtxDrawing); - template static void update_delete_drawing( ACtxDrawing& rCtxDraw, IT_T const& first, IT_T const& last); diff --git a/src/osp/Active/opengl/SysRenderGL.cpp b/src/osp/Active/opengl/SysRenderGL.cpp index 079e2b73..f198cc05 100644 --- a/src/osp/Active/opengl/SysRenderGL.cpp +++ b/src/osp/Active/opengl/SysRenderGL.cpp @@ -209,108 +209,102 @@ void SysRenderGL::compile_resource_meshes( } } -void SysRenderGL::assign_meshes( +void SysRenderGL::sync_drawent_mesh( + DrawEnt const ent, KeyedVec const& cmpMeshIds, IdMap_t const& meshToRes, - std::vector const& entsDirty, MeshGlEntStorage_t& rCmpMeshGl, RenderGL& rRenderGl) { - for (DrawEnt const ent : entsDirty) + ACompMeshGl &rEntMeshGl = rCmpMeshGl[ent]; + MeshIdOwner_t const& entMeshScnId = cmpMeshIds[ent]; + + // Make sure dirty entity has a MeshId component + if (entMeshScnId.has_value()) { - ACompMeshGl &rEntMeshGl = rCmpMeshGl[ent]; - MeshIdOwner_t const& entMeshScnId = cmpMeshIds[ent]; + // Check if scene mesh ID is properly synchronized + if (rEntMeshGl.m_scnId == entMeshScnId) + { + return; // No changes needed + } + + rEntMeshGl.m_scnId = entMeshScnId; + + // Check if MeshId is associated with a resource + if (auto const& foundIt = meshToRes.find(entMeshScnId); + foundIt != meshToRes.end()) + { + ResId const meshResId = foundIt->second; - // Make sure dirty entity has a MeshId component - if (entMeshScnId.has_value()) + // Mesh should have been loaded beforehand, assign it! + rEntMeshGl.m_glId = rRenderGl.m_resToMesh.at(meshResId); + } + else + { + OSP_LOG_WARN("No mesh data found for Mesh {} from Entity {}", + std::size_t(entMeshScnId), std::size_t(ent)); + } + } + else + { + if (rEntMeshGl.m_glId != lgrn::id_null()) { - // Check if scene mesh ID is properly synchronized - if (rEntMeshGl.m_scnId == entMeshScnId) - { - continue; // No changes needed - } - - rEntMeshGl.m_scnId = entMeshScnId; - - // Check if MeshId is associated with a resource - if (auto const& foundIt = meshToRes.find(entMeshScnId); - foundIt != meshToRes.end()) - { - ResId const meshResId = foundIt->second; - - // Mesh should have been loaded beforehand, assign it! - rEntMeshGl.m_glId = rRenderGl.m_resToMesh.at(meshResId); - } - else - { - OSP_LOG_WARN("No mesh data found for Mesh {} from Entity {}", - std::size_t(entMeshScnId), std::size_t(ent)); - } + // ACompMesh removed, remove ACompMeshGL too + rEntMeshGl = {}; } else { - if (rEntMeshGl.m_glId != lgrn::id_null()) - { - // ACompMesh removed, remove ACompMeshGL too - rEntMeshGl = {}; - } - else - { - // Why is this entity here? - } + // Why is this entity here? } } } -void SysRenderGL::assign_textures( +void SysRenderGL::sync_drawent_texture( + DrawEnt const ent, KeyedVec const& cmpTexIds, IdMap_t const& texToRes, - std::vector const& entsDirty, TexGlEntStorage_t& rCmpTexGl, RenderGL& rRenderGl) { - for (DrawEnt const ent : entsDirty) + ACompTexGl &rEntTexGl = rCmpTexGl[ent]; + TexIdOwner_t const& entTexScnId = cmpTexIds[ent]; + + // Make sure dirty entity has a MeshId component + if (entTexScnId.has_value()) { - ACompTexGl &rEntTexGl = rCmpTexGl[ent]; - TexIdOwner_t const& entTexScnId = cmpTexIds[ent]; + // Check if scene mesh ID is properly synchronized + if (rEntTexGl.m_scnId == entTexScnId) + { + return; // No changes needed + } + + rEntTexGl.m_scnId = entTexScnId; + + // Check if MeshId is associated with a resource + if (auto const& foundIt = texToRes.find(entTexScnId); + foundIt != texToRes.end()) + { + ResId const texResId = foundIt->second; - // Make sure dirty entity has a MeshId component - if (entTexScnId.has_value()) + // Mesh should have been loaded beforehand, assign it! + rEntTexGl.m_glId = rRenderGl.m_resToTex.at(texResId); + } + else + { + OSP_LOG_WARN("No mesh data found for Mesh {} from Entity {}", + std::size_t(entMeshScnId), std::size_t(ent)); + } + } + else + { + if (rEntTexGl.m_glId != lgrn::id_null()) { - // Check if scene mesh ID is properly synchronized - if (rEntTexGl.m_scnId == entTexScnId) - { - continue; // No changes needed - } - - rEntTexGl.m_scnId = entTexScnId; - - // Check if MeshId is associated with a resource - if (auto const& foundIt = texToRes.find(entTexScnId); - foundIt != texToRes.end()) - { - ResId const texResId = foundIt->second; - - // Mesh should have been loaded beforehand, assign it! - rEntTexGl.m_glId = rRenderGl.m_resToTex.at(texResId); - } - else - { - OSP_LOG_WARN("No mesh data found for Mesh {} from Entity {}", - std::size_t(entMeshScnId), std::size_t(ent)); - } + // ACompMesh removed, remove ACompMeshGL too + rEntTexGl = {}; } else { - if (rEntTexGl.m_glId != lgrn::id_null()) - { - // ACompMesh removed, remove ACompMeshGL too - rEntTexGl = {}; - } - else - { - // Why is this entity here? - } + // Why is this entity here? } } } diff --git a/src/osp/Active/opengl/SysRenderGL.h b/src/osp/Active/opengl/SysRenderGL.h index ac66e0fb..3a5f2537 100644 --- a/src/osp/Active/opengl/SysRenderGL.h +++ b/src/osp/Active/opengl/SysRenderGL.h @@ -160,21 +160,36 @@ class SysRenderGL RenderGL& rRenderGl); /** - * @brief Synchronize entities with a MeshId component to an ACompMeshGl + * @brief Synchronize an entity's MeshId component to an ACompMeshGl * + * @param ent [in] DrawEnt with mesh to synchronize * @param cmpMeshIds [in] Scene Mesh Id component * @param meshToRes [in] Scene's Mesh Id to Resource Id - * @param entsDirty [in] Entities to synchronize * @param rCmpMeshGl [ref] Renderer-side ACompMeshGl components * @param rRenderGl [ref] Renderer state */ - static void assign_meshes( + static void sync_drawent_mesh( + DrawEnt ent, KeyedVec const& cmpMeshIds, IdMap_t const& meshToRes, - std::vector const& entsDirty, MeshGlEntStorage_t& rCmpMeshGl, RenderGL& rRenderGl); + template + static void sync_drawent_mesh( + ITA_T const& first, + ITB_T const& last, + KeyedVec const& cmpMeshIds, + IdMap_t const& meshToRes, + MeshGlEntStorage_t& rCmpMeshGl, + RenderGL& rRenderGl) + { + std::for_each(first, last, [&] (DrawEnt const ent) + { + sync_drawent_mesh(ent, cmpMeshIds, meshToRes, rCmpMeshGl, rRenderGl); + }); + } + /** * @brief Synchronize entities with a TexId component to an ACompTexGl * @@ -184,13 +199,28 @@ class SysRenderGL * @param rCmpTexGl [ref] Renderer-side ACompTexGl components * @param rRenderGl [ref] Renderer state */ - static void assign_textures( + static void sync_drawent_texture( + DrawEnt ent, KeyedVec const& cmpTexIds, IdMap_t const& texToRes, - std::vector const& entsDirty, TexGlEntStorage_t& rCmpTexGl, RenderGL& rRenderGl); + template + static void sync_drawent_texture( + ITA_T const& first, + ITB_T const& last, + KeyedVec const& cmpTexIds, + IdMap_t const& texToRes, + TexGlEntStorage_t& rCmpTexGl, + RenderGL& rRenderGl) + { + std::for_each(first, last, [&] (DrawEnt const ent) + { + sync_drawent_texture(ent, cmpTexIds, texToRes, rCmpTexGl, rRenderGl); + }); + } + /** * @brief Call draw functions of a RenderGroup of opaque objects * diff --git a/src/osp/Shaders/MeshVisualizer.cpp b/src/osp/Shaders/MeshVisualizer.cpp index cf6cc3aa..e10e0d31 100644 --- a/src/osp/Shaders/MeshVisualizer.cpp +++ b/src/osp/Shaders/MeshVisualizer.cpp @@ -78,3 +78,30 @@ void shader::draw_ent_visualizer( } } +void shader::sync_drawent_visualizer( + active::DrawEnt const ent, + active::DrawEntSet_t const& hasMaterial, + active::RenderGroup::Storage_t& rStorage, + ACtxDrawMeshVisualizer &rData) +{ + using namespace active; + + bool alreadyAdded = rStorage.contains(ent); + if (hasMaterial.test(std::size_t(ent))) + { + if ( ! alreadyAdded) + { + rStorage.emplace( ent, EntityToDraw{&draw_ent_visualizer, {&rData} } ); + } + } + else + { + if (alreadyAdded) + { + rStorage.erase(ent); + } + } +} + + + diff --git a/src/osp/Shaders/MeshVisualizer.h b/src/osp/Shaders/MeshVisualizer.h index e4c53605..f2fabbfb 100644 --- a/src/osp/Shaders/MeshVisualizer.h +++ b/src/osp/Shaders/MeshVisualizer.h @@ -55,43 +55,28 @@ struct ACtxDrawMeshVisualizer }; void draw_ent_visualizer( - active::DrawEnt ent, - active::ViewProjMatrix const& viewProj, - active::EntityToDraw::UserData_t userData) noexcept; - -template -void sync_visualizer( - ITA_T dirtyFirst, - ITB_T const& dirtyLast, - active::DrawEntSet_t const& hasMaterial, - active::RenderGroup::Storage_t& rStorage, - ACtxDrawMeshVisualizer &rData) + active::DrawEnt ent, + active::ViewProjMatrix const& viewProj, + active::EntityToDraw::UserData_t userData) noexcept; + +void sync_drawent_visualizer( + active::DrawEnt const ent, + active::DrawEntSet_t const& hasMaterial, + active::RenderGroup::Storage_t& rStorage, + ACtxDrawMeshVisualizer& rData); + +template +static void sync_drawent_visualizer( + ITA_T const& first, + ITB_T const& last, + active::DrawEntSet_t const& hasMaterial, + active::RenderGroup::Storage_t& rStorage, + ACtxDrawMeshVisualizer& rData) { - using namespace active; - - while (dirtyFirst != dirtyLast) + std::for_each(first, last, [&] (active::DrawEnt const ent) { - DrawEnt const ent = *dirtyFirst; - bool alreadyAdded = rStorage.contains(ent); - if (hasMaterial.test(std::size_t(ent))) - { - if ( ! alreadyAdded) - { - rStorage.emplace( ent, EntityToDraw{&draw_ent_visualizer, {&rData} } ); - } - } - else - { - if (alreadyAdded) - { - rStorage.erase(ent); - } - } - - std::advance(dirtyFirst, 1); - } - - + sync_drawent_visualizer(ent, hasMaterial, rStorage, rData); + }); } } // namespace osp::shader diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 768bc679..a2d24a2b 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -255,6 +255,8 @@ static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext if (rerunLoop) { + rExecPl.canceled = false; + rExec.plAdvanceNext.set(std::size_t(pipeline)); exec_log(rExec, ExecContext::PipelineLoop{pipeline}); } diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index 17b76f30..8cf784f3 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -241,41 +241,41 @@ std::ostream& operator<<(std::ostream& rStream, TopExecWriteLog const& write) } else if constexpr (std::is_same_v) { - rStream << " PipelineRun PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; + rStream << " PipelineRun PL" << std::setw(3) << std::left << PipelineInt(msg.pipeline) << "\n"; } else if constexpr (std::is_same_v) { - rStream << " PipelineFinish PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; + rStream << " PipelineFinish PL" << std::setw(3) << std::left << PipelineInt(msg.pipeline) << "\n"; } else if constexpr (std::is_same_v) { - rStream << " PipelineCancel PL" << std::setw(3) << PipelineInt(msg.pipeline) << "(" + rStream << " PipelineCancel PL" << std::setw(3) << std::left << PipelineInt(msg.pipeline) << "(" << stage_name(msg.pipeline, msg.stage) << ")\n"; } else if constexpr (std::is_same_v) { - rStream << " PipelineLoop PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; + rStream << " PipelineLoop PL" << std::setw(3) << std::left << PipelineInt(msg.pipeline) << "\n"; } else if constexpr (std::is_same_v) { - rStream << " PipelineLoopFinish PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; + rStream << " PipelineLoopFinish PL" << std::setw(3) << std::left << PipelineInt(msg.pipeline) << "\n"; } else if constexpr (std::is_same_v) { - rStream << " StageChange PL" << std::setw(3) << PipelineInt(msg.pipeline) + rStream << " StageChange PL" << std::setw(3) << std::left << PipelineInt(msg.pipeline) << "(" << stage_name(msg.pipeline, msg.stageOld) << " -> " << stage_name(msg.pipeline, msg.stageNew) << ")\n"; } else if constexpr (std::is_same_v) { rStream << " Enqueue " << (msg.blocked ? "Blocked" : "Run") - << " on PL" << std::setw(3) << PipelineInt(msg.pipeline) + << " on PL" << std::setw(3) << std::left << PipelineInt(msg.pipeline) << "(" << stage_name(msg.pipeline, msg.stage) << ")" << " TASK" << TaskInt(msg.task) << " - " << taskData[msg.task].m_debugName << "\n"; } else if constexpr (std::is_same_v) { rStream << " * " << (msg.satisfied ? "[DONE]" : "[wait]") << "Require PL" - << std::setw(3) << PipelineInt(msg.pipeline) + << std::setw(3) << std::left << PipelineInt(msg.pipeline) << "(" << stage_name(msg.pipeline, msg.stage) << ")\n"; } else if constexpr (std::is_same_v) @@ -288,11 +288,11 @@ std::ostream& operator<<(std::ostream& rStream, TopExecWriteLog const& write) } else if constexpr (std::is_same_v) { - rStream << "ExternalRunRequest PL" << std::setw(3) << PipelineInt(msg.pipeline) << "\n"; + rStream << "ExternalRunRequest PL" << std::setw(3) << std::left << PipelineInt(msg.pipeline) << "\n"; } else if constexpr (std::is_same_v) { - rStream << "ExternalSignal PL" << std::setw(3) << PipelineInt(msg.pipeline) << (msg.ignored ? " IGNORED!" : " ") << "\n"; + rStream << "ExternalSignal PL" << std::setw(3) << std::left << PipelineInt(msg.pipeline) << (msg.ignored ? " IGNORED!" : " ") << "\n"; } }; diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index a1f1b1ee..28531b0c 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -304,7 +304,8 @@ struct PlMagnum { PipelineDef cleanup {"cleanup Cleanup Magnum"}; - PipelineDef sync {"render"}; + PipelineDef sync {"sync"}; + PipelineDef resync {"resync"}; PipelineDef meshGL {"meshGL"}; PipelineDef textureGL {"textureGL"}; diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index ca8bed31..29abe902 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -89,10 +89,12 @@ struct CommonMagnumApp : IOspApplication m_rMainLoopCtrl = MainLoopControl{ .doUpdate = true, .doSync = true, - .doResync = false, + .doResync = m_justStarting, .doRender = true, }; + m_justStarting = false; + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_mainLoop); m_rTestApp.m_pExecutor->signal(m_rTestApp, m_inputs); m_rTestApp.m_pExecutor->signal(m_rTestApp, m_sceneUpdate); @@ -130,11 +132,13 @@ struct CommonMagnumApp : IOspApplication TestApp &m_rTestApp; MainLoopControl &m_rMainLoopCtrl; - PipelineId m_mainLoop; - PipelineId m_inputs; - PipelineId m_renderSync; - PipelineId m_sceneUpdate; - PipelineId m_sceneRender; + PipelineId m_mainLoop; + PipelineId m_inputs; + PipelineId m_renderSync; + PipelineId m_sceneUpdate; + PipelineId m_sceneRender; + + bool m_justStarting{true}; }; static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& scnRenderer, std::vector run = {}) diff --git a/src/test_application/activescenes/scenarios_enginetest.cpp b/src/test_application/activescenes/scenarios_enginetest.cpp index bf5b0528..15442921 100644 --- a/src/test_application/activescenes/scenarios_enginetest.cpp +++ b/src/test_application/activescenes/scenarios_enginetest.cpp @@ -175,7 +175,8 @@ entt::any setup_scene(osp::Resources& rResources, osp::PkgId const pkg) void update_test_scene(EngineTestScene& rScene, float const delta) { // Clear drawing-related dirty flags/vectors - osp::active::SysRender::clear_dirty_all(rScene.m_drawing); + rScene.m_drawing.m_meshDirty.clear(); + rScene.m_drawing.m_diffuseDirty.clear(); rScene.m_matPhongDirty.clear(); // Rotate the cube @@ -248,14 +249,22 @@ void sync_test_scene( SysRenderGL::compile_resource_textures(rScene.m_drawingRes, *rScene.m_pResources, rRenderGl); // Assign GL meshes to entities with a mesh component - SysRenderGL::assign_meshes( - rScene.m_drawing.m_mesh, rScene.m_drawingRes.m_meshToRes, rScene.m_drawing.m_meshDirty, - rRenderer.m_sceneRenderGL.m_meshId, rRenderGl); + SysRenderGL::sync_drawent_mesh( + rScene.m_drawing.m_meshDirty.begin(), + rScene.m_drawing.m_meshDirty.end(), + rScene.m_drawing.m_mesh, + rScene.m_drawingRes.m_meshToRes, + rRenderer.m_sceneRenderGL.m_meshId, + rRenderGl); // Assign GL textures to entities with a texture component - SysRenderGL::assign_textures( - rScene.m_drawing.m_diffuseTex, rScene.m_drawingRes.m_texToRes, rScene.m_drawing.m_diffuseDirty, - rRenderer.m_sceneRenderGL.m_diffuseTexId, rRenderGl); + SysRenderGL::sync_drawent_texture( + rScene.m_drawing.m_meshDirty.begin(), + rScene.m_drawing.m_meshDirty.end(), + rScene.m_drawing.m_diffuseTex, + rScene.m_drawingRes.m_texToRes, + rRenderer.m_sceneRenderGL.m_diffuseTexId, + rRenderGl); // Calculate hierarchy transforms diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index 543e59f5..a515c563 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -58,15 +58,15 @@ Session setup_scene( rBuilder.pipeline(plScn.update).parent(tgApp.mainLoop).wait_for_signal(ModifyOrSignal); -// rBuilder.task() -// .name ("Schedule Scene update") -// .schedules ({plScn.update(Schedule)}) -// .push_to (out.m_tasks) -// .args ({ idMainLoopCtrl}) -// .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions -// { -// return rMainLoopCtrl.doUpdate ? osp::TaskActions{} : osp::TaskAction::Cancel; -// }); + rBuilder.task() + .name ("Schedule Scene update") + .schedules ({plScn.update(Schedule)}) + .push_to (out.m_tasks) + .args ({ idMainLoopCtrl}) + .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions + { + return rMainLoopCtrl.doUpdate ? osp::TaskActions{} : osp::TaskAction::Cancel; + }); return out; } @@ -244,6 +244,28 @@ Session setup_common_scene( } }); + rBuilder.task() + .name ("Clear dirty DrawEnt's textures once we're done with it") + .run_on ({tgCS.entMeshDirty(Clear)}) + .push_to (out.m_tasks) + .args ({ idDrawing}) + .func([] (ACtxDrawing& rDrawing) noexcept + { + rDrawing.m_meshDirty.clear(); + }); + + rBuilder.task() + .name ("Clear dirty DrawEnt's textures once we're done with it") + .run_on ({tgCS.entTextureDirty(Clear)}) + .push_to (out.m_tasks) + .args ({ idDrawing}) + .func([] (ACtxDrawing& rDrawing) noexcept + { + rDrawing.m_diffuseDirty.clear(); + }); + + // Clean up tasks + rBuilder.task() .name ("Clean up scene and resource owners") .run_on ({tgScn.cleanup(Run_)}) diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index ec861619..b470c97b 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -146,7 +146,9 @@ Session setup_shape_spawn( rBuilder.task() .name ("Add mesh and material to spawned shapes") .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New), tgCS.drawEntResized(ModifyOrSignal), tgCS.materialDirty(Modify_)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), + tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New), tgCS.drawEntResized(ModifyOrSignal), + tgCS.materialDirty(Modify_), tgCS.entMeshDirty(Modify_)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idSpawner, idNMesh }) .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index ec3cc094..ee6e70c3 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -101,7 +101,8 @@ Session setup_magnum( auto const tgMgn = out.create_pipelines(rBuilder); out.m_cleanup = tgMgn.cleanup; - rBuilder.pipeline(tgMgn.sync).parent(tgApp.mainLoop).wait_for_signal(ModifyOrSignal); + rBuilder.pipeline(tgMgn.sync) .parent(tgApp.mainLoop).wait_for_signal(ModifyOrSignal); + rBuilder.pipeline(tgMgn.resync).parent(tgApp.mainLoop); rBuilder.pipeline(tgMgn.meshGL) .parent(tgMgn.sync); rBuilder.pipeline(tgMgn.textureGL) .parent(tgMgn.sync); @@ -125,6 +126,27 @@ Session setup_magnum( rRenderGl = {}; // Needs the OpenGL thread for destruction }); + rBuilder.task() + .name ("Schedule GL Sync") + .schedules ({tgMgn.sync(Schedule)}) + .push_to (out.m_tasks) + .args ({ idMainLoopCtrl}) + .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions + { + return rMainLoopCtrl.doSync ? osp::TaskActions{} : osp::TaskAction::Cancel; + }); + + rBuilder.task() + .name ("Schedule GL Resync") + .schedules ({tgMgn.resync(Schedule)}) + .push_to (out.m_tasks) + .args ({ idMainLoopCtrl}) + .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions + { + return rMainLoopCtrl.doResync ? osp::TaskActions{} : osp::TaskAction::Cancel; + }); + + return out; } @@ -189,7 +211,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Compile Resource Meshes to GL") .run_on ({tgCS.meshResDirty(UseOrRun)}) - .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(New), tgCS.entMeshDirty(Modify_)}) + .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(New), tgCS.entMeshDirty(UseOrRun)}) .push_to (out.m_tasks) .args ({ idDrawingRes, idResources, idRenderGl }) .func([] (ACtxDrawingRes const& rDrawingRes, osp::Resources& rResources, RenderGL& rRenderGl) noexcept @@ -220,36 +242,86 @@ Session setup_scene_renderer( }); rBuilder.task() - .name ("Assign GL textures to entities with scene textures") + .name ("Sync GL textures to entities with scene textures") .run_on ({tgCS.entTextureDirty(UseOrRun)}) .sync_with ({tgCS.texture(Ready), tgMgn.textureGL(Ready), tgMgn.entTextureGL(Modify), tgCS.drawEntResized(Done)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept { - SysRenderGL::assign_textures(rDrawing.m_diffuseTex, rDrawingRes.m_texToRes, rDrawing.m_diffuseDirty, rScnRender.m_diffuseTexId, rRenderGl); + SysRenderGL::sync_drawent_texture( + rDrawing.m_diffuseDirty.begin(), + rDrawing.m_diffuseDirty.end(), + rDrawing.m_diffuseTex, + rDrawingRes.m_texToRes, + rScnRender.m_diffuseTexId, + rRenderGl); }); -// rBuilder.task() -// .name ("Schedule Assign GL meshes") -// .schedules ({tgCS.entMeshDirty(Schedule_)}) -// .sync_with ({tgCS.mesh(Ready)}) -// .push_to (out.m_tasks) -// .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) -// .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept -> TaskActions -// { -// return rDrawing.m_meshDirty.empty() ? TaskAction::Cancel : TaskActions{}; -// }); + rBuilder.task() + .name ("Resync GL textures") + .run_on ({tgMgn.resync(Run)}) + .sync_with ({tgCS.texture(Ready), tgMgn.textureGL(Ready), tgMgn.entTextureGL(Modify), tgCS.drawEntResized(Done)}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) + .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept + { + for (auto const drawEntInt : rDrawing.m_drawIds.bitview().zeros()) + { + SysRenderGL::sync_drawent_texture( + DrawEnt(drawEntInt), + rDrawing.m_diffuseTex, + rDrawingRes.m_texToRes, + rScnRender.m_diffuseTexId, + rRenderGl); + } + }); + + rBuilder.task() + .name ("Schedule Assign GL meshes") + .schedules ({tgCS.entMeshDirty(Schedule_)}) + .sync_with ({tgCS.mesh(Ready)}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) + .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept -> TaskActions + { + return rDrawing.m_meshDirty.empty() ? TaskAction::Cancel : TaskActions{}; + }); rBuilder.task() - .name ("Assign GL meshes to entities with scene meshes") + .name ("Sync GL meshes to entities with scene meshes") .run_on ({tgCS.entMeshDirty(UseOrRun)}) .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(Ready), tgMgn.entMeshGL(Modify), tgCS.drawEntResized(Done)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept { - SysRenderGL::assign_meshes(rDrawing.m_mesh, rDrawingRes.m_meshToRes, rDrawing.m_meshDirty, rScnRender.m_meshId, rRenderGl); + SysRenderGL::sync_drawent_mesh( + rDrawing.m_meshDirty.begin(), + rDrawing.m_meshDirty.end(), + rDrawing.m_mesh, + rDrawingRes.m_meshToRes, + rScnRender.m_meshId, + rRenderGl); + }); + + rBuilder.task() + .name ("Resync GL meshes") + .run_on ({tgMgn.resync(Run)}) + .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(Ready), tgMgn.entMeshGL(Modify), tgCS.drawEntResized(Done)}) + .push_to (out.m_tasks) + .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) + .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept + { + for (auto const drawEntInt : rDrawing.m_drawIds.bitview().zeros()) + { + SysRenderGL::sync_drawent_mesh( + DrawEnt(drawEntInt), + rDrawing.m_mesh, + rDrawingRes.m_meshToRes, + rScnRender.m_meshId, + rRenderGl); + } }); rBuilder.task() @@ -365,14 +437,29 @@ Session setup_shader_visualizer( rBuilder.task() .name ("Sync MeshVisualizer shader entities") - .run_on ({tgCS.materialDirty(UseOrRun)}) + .run_on ({tgMgn.sync(Run)}) + .sync_with ({tgSR.groupEnts(Modify), tgSR.group(Modify), tgCS.materialDirty(UseOrRun)}) + .push_to (out.m_tasks) + .args ({ idDrawing, idGroupFwd, idDrawShVisual}) + .func([] (ACtxDrawing const& rDrawing, RenderGroup& rGroupFwd, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept + { + Material const &rMat = rDrawing.m_materials[rDrawShVisual.m_materialId]; + sync_drawent_visualizer(rMat.m_dirty.begin(), rMat.m_dirty.end(), rMat.m_ents, rGroupFwd.m_entities, rDrawShVisual); + }); + + rBuilder.task() + .name ("Resync MeshVisualizer") + .run_on ({tgMgn.resync(Run)}) .sync_with ({tgSR.groupEnts(Modify), tgSR.group(Modify)}) .push_to (out.m_tasks) .args ({ idDrawing, idGroupFwd, idDrawShVisual}) .func([] (ACtxDrawing const& rDrawing, RenderGroup& rGroupFwd, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept { Material const &rMat = rDrawing.m_materials[rDrawShVisual.m_materialId]; - sync_visualizer(rMat.m_dirty.begin(), rMat.m_dirty.end(), rMat.m_ents, rGroupFwd.m_entities, rDrawShVisual); + for (auto const drawEntInt : rMat.m_ents.ones()) + { + sync_drawent_visualizer(DrawEnt(drawEntInt), rMat.m_ents, rGroupFwd.m_entities, rDrawShVisual); + } }); return out; From 1025ae8f52064e207269cfa9929cd53ca507972e Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 27 Aug 2023 18:04:47 -0700 Subject: [PATCH 34/35] Fully fix physics test scene --- src/osp/Active/SysSceneGraph.h | 21 +++ src/osp/Active/drawing.h | 2 +- src/osp/tasks/builder.h | 11 +- src/osp/tasks/tasks.h | 7 + src/osp/tasks/top_execute.cpp | 7 +- src/osp/tasks/top_session.h | 9 +- .../activescenes/identifiers.h | 56 ++++-- .../activescenes/scenarios.cpp | 25 +-- src/test_application/activescenes/scenarios.h | 1 + .../activescenes/scene_common.cpp | 37 ++-- .../activescenes/scene_misc.cpp | 159 +++++++++++++++--- .../activescenes/scene_misc.h | 12 ++ .../activescenes/scene_newton.cpp | 71 ++++---- .../activescenes/scene_physics.cpp | 92 +--------- .../activescenes/scene_physics.h | 11 -- .../activescenes/scene_renderer.cpp | 11 +- src/test_application/main.cpp | 8 +- src/test_application/testapp.cpp | 3 + 18 files changed, 317 insertions(+), 226 deletions(-) diff --git a/src/osp/Active/SysSceneGraph.h b/src/osp/Active/SysSceneGraph.h index f0b2b7ee..6ce9f15b 100644 --- a/src/osp/Active/SysSceneGraph.h +++ b/src/osp/Active/SysSceneGraph.h @@ -160,6 +160,12 @@ class SysSceneGraph template static void cut(ACtxSceneGraph& rScnGraph, ITA_T first, ITB_T const& last); + /** + * @brief Add multiple entities and their descendents to a delete queue + */ + template + static void queue_delete_entities(ACtxSceneGraph& rScnGraph, ActiveEntVec_t &rDelete, ITA_T const& first, ITB_T const& last); + private: static void do_delete(ACtxSceneGraph& rScnGraph); @@ -186,5 +192,20 @@ void SysSceneGraph::cut(ACtxSceneGraph& rScnGraph, ITA_T first, ITB_T const& las do_delete(rScnGraph); } +template +void SysSceneGraph::queue_delete_entities(ACtxSceneGraph& rScnGraph, ActiveEntVec_t &rDelete, ITA_T const& first, ITB_T const& last) +{ + std::for_each(first, last, [&] (ActiveEnt const ent) + { + rDelete.push_back(ent); + for (ActiveEnt const descendent : SysSceneGraph::descendants(rScnGraph, ent)) + { + rDelete.push_back(descendent); + } + }); + + SysSceneGraph::cut(rScnGraph, first, last); +} + } // namespace osp::active diff --git a/src/osp/Active/drawing.h b/src/osp/Active/drawing.h index 1f6418ba..6f93addc 100644 --- a/src/osp/Active/drawing.h +++ b/src/osp/Active/drawing.h @@ -115,7 +115,7 @@ struct ACtxDrawing lgrn::IdRegistryStl m_texIds; TexRefCount_t m_texRefCounts; - // Meshes and textures assigned to ActiveEnts + // Meshes and textures assigned to DrawEnts KeyedVec m_diffuseTex; std::vector m_diffuseDirty; diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 8b52b942..503d4a08 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -98,24 +98,25 @@ struct TaskBuilderBase m_rTasks.m_pipelineControl.resize(capacity); m_rTasks.m_pipelineParents.resize(capacity, lgrn::id_null()); - TGT_STRUCT_T out; - // Set m_value members of TGT_STRUCT_T, asserted to contain only PipelineDef<...> // This is janky enough that rewriting the code below might cause it to ONLY SEGFAULT ON // RELEASE AND ISN'T CAUGHT BY ASAN WTF??? (on gcc 11) - auto *pOutBytes = reinterpret_cast(std::addressof(out)); + + alignas(TGT_STRUCT_T) std::array bytes; + TGT_STRUCT_T *pOut = new(bytes.data()) TGT_STRUCT_T; for (std::size_t i = 0; i < count; ++i) { PipelineId const pl = pipelinesOut[i]; - unsigned char *pDefBytes = pOutBytes + sizeof(PipelineDefBlank_t)*i; + unsigned char *pDefBytes = bytes.data() + sizeof(PipelineDefBlank_t)*i; *reinterpret_cast(pDefBytes + offsetof(PipelineDefBlank_t, m_value)) = pl; m_rTasks.m_pipelineInfo[pl].stageType = *reinterpret_cast(pDefBytes + offsetof(PipelineDefBlank_t, m_type)); m_rTasks.m_pipelineInfo[pl].name = *reinterpret_cast(pDefBytes + offsetof(PipelineDefBlank_t, m_name)); } - return out; + + return *pOut; } template diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 37c1a729..76320d88 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -83,6 +83,13 @@ struct PipelineInfo static inline KeyedVec> sm_stageNames; + template + static inline constexpr void register_stage_enum() + { + PipelineInfo::stage_type_t const type = PipelineInfo::stage_type_family_t::value; + PipelineInfo::sm_stageNames[type] = stage_names(STAGE_ENUM_T{}); + } + std::string_view name; std::string_view category; stage_type_t stageType; diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index 8cf784f3..512b2bd6 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -110,6 +110,8 @@ std::ostream& operator<<(std::ostream& rStream, TopExecWriteState const& write) { ExecPipeline const &plExec = exec.plData[pipeline]; + std::cout << "pipeline: " << int(pipeline) << "\n"; + for (int i = 0; i < depth; ++i) { rStream << "- "; @@ -148,7 +150,7 @@ std::ostream& operator<<(std::ostream& rStream, TopExecWriteState const& write) int charsUsed = 7; // "PL###" + ": " - for (int stage = 0; stage < stageCount; ++stage) + for (int stage = 0; stage < std::min(stageNames.size(), stageCount); ++stage) { bool const sel = int(plExec.stage) == stage; rStream << (sel ? '[' : ' ') @@ -162,8 +164,9 @@ std::ostream& operator<<(std::ostream& rStream, TopExecWriteState const& write) { rStream << ' '; } + std::cout << "AAAA" << info.name; - rStream << " | " << info.name; + rStream << " | " << (info.name.empty() ? "untitled or deleted" : info.name); rStream << "\n"; }; diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index a17a1839..e59b410a 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -107,15 +107,16 @@ struct Session info.hash_code(), info.name(), m_pipelines.size()); - TGT_STRUCT_T out; - auto const members = Corrade::Containers::staticArrayView(reinterpret_cast(&out)); + alignas(TGT_STRUCT_T) std::array bytes; + TGT_STRUCT_T *pOut = new(bytes.data()) TGT_STRUCT_T;\ for (std::size_t i = 0; i < count; ++i) { - members[i].m_value = m_pipelines[i]; + unsigned char *pDefBytes = bytes.data() + sizeof(PipelineDefBlank_t)*i; + *reinterpret_cast(pDefBytes + offsetof(PipelineDefBlank_t, m_value)) = m_pipelines[i]; } - return out; + return *pOut; } std::vector m_data; diff --git a/src/test_application/activescenes/identifiers.h b/src/test_application/activescenes/identifiers.h index 28531b0c..828457d9 100644 --- a/src/test_application/activescenes/identifiers.h +++ b/src/test_application/activescenes/identifiers.h @@ -52,7 +52,7 @@ OSP_DECLARE_STAGE_NAMES(EStgEvnt, "Run", "Done"); OSP_DECLARE_STAGE_NO_SCHEDULE(EStgEvnt); /** - * @brief Temporary queue / events that are filled, used, then cleared right away + * @brief Intermediate container that is filled, used, then cleared right away */ enum class EStgIntr : uint8_t { @@ -65,6 +65,19 @@ enum class EStgIntr : uint8_t OSP_DECLARE_STAGE_NAMES(EStgIntr, "Resize", "Modify", "Schedule", "Use/Run", "Clear"); OSP_DECLARE_STAGE_SCHEDULE(EStgIntr, EStgIntr::Schedule_); +/** + * @brief 'Reversed' Intermediate container + */ +enum class EStgRevd : uint8_t +{ + Schedule__, + UseOrRun_, + Clear_, + Resize_, + Modify__, +}; +OSP_DECLARE_STAGE_NAMES(EStgRevd, "Schedule", "Use/Run", "Clear", "Resize", "Modify"); +OSP_DECLARE_STAGE_SCHEDULE(EStgRevd, EStgRevd::Schedule__); /** * @brief Continuous Containers, data that persists and is modified over time @@ -72,6 +85,9 @@ OSP_DECLARE_STAGE_SCHEDULE(EStgIntr, EStgIntr::Schedule_); */ enum class EStgCont : uint8_t { + Prev, + ///< Previous state of container + Delete, ///< Remove elements from a container or mark them for deletion. This often involves reading ///< a set of elements to delete. This is run first since it leaves empty spaces for new @@ -86,7 +102,7 @@ enum class EStgCont : uint8_t Ready ///< Container is ready to use }; -OSP_DECLARE_STAGE_NAMES(EStgCont, "Delete", "New", "Modify", "Use"); +OSP_DECLARE_STAGE_NAMES(EStgCont, "Prev", "Delete", "New", "Modify", "Use"); OSP_DECLARE_STAGE_NO_SCHEDULE(EStgCont); enum class EStgFBO @@ -98,6 +114,19 @@ enum class EStgFBO OSP_DECLARE_STAGE_NAMES(EStgFBO, "Bind", "Draw", "Unbind"); OSP_DECLARE_STAGE_NO_SCHEDULE(EStgFBO); +//----------------------------------------------------------------------------- + +inline void register_stage_enums() +{ + osp::PipelineInfo::sm_stageNames.resize(32); + osp::PipelineInfo::register_stage_enum(); + osp::PipelineInfo::register_stage_enum(); + osp::PipelineInfo::register_stage_enum(); + osp::PipelineInfo::register_stage_enum(); + osp::PipelineInfo::register_stage_enum(); + osp::PipelineInfo::register_stage_enum(); +} + using osp::PipelineDef; //----------------------------------------------------------------------------- @@ -106,7 +135,7 @@ using osp::PipelineDef; idResources, idMainLoopCtrl struct PlApplication { - PipelineDef mainLoop {"mainLoop - ..."}; + PipelineDef mainLoop {"mainLoop"}; }; //----------------------------------------------------------------------------- @@ -116,27 +145,30 @@ struct PlApplication struct PlScene { PipelineDef cleanup {"cleanup - Scene cleanup before destruction"}; - PipelineDef update {"update - main loop "}; + PipelineDef update {"update"}; }; #define TESTAPP_DATA_COMMON_SCENE 6, \ idBasic, idDrawing, idDrawingRes, idActiveEntDel, idDrawEntDel, idNMesh struct PlCommonScene { - PipelineDef activeEnt {"activeEnt"}; + PipelineDef activeEnt {"activeEnt - ActiveEnt ID Registry"}; PipelineDef activeEntResized {"activeEntResized"}; - PipelineDef activeEntDelete {"activeEntDelete"}; + PipelineDef activeEntDelete {"activeEntDelete - Vector of ActiveEnts that need to be deleted"}; PipelineDef transform {"transform"}; PipelineDef hierarchy {"hierarchy"}; PipelineDef drawEnt {"drawEnt"}; PipelineDef drawEntResized {"drawEntResized"}; - PipelineDef drawEntDelete {"drawEntDelete"}; + PipelineDef drawEntDelete {"drawEntDelete - Vector of DrawEnts that need to be deleted"}; PipelineDef mesh {"mesh"}; PipelineDef texture {"texture"}; + PipelineDef entMesh {"entMesh - Scene-side DrawEnt-MeshId association"}; + PipelineDef entTexture {"entTexture - Scene-side DrawEnt-TexId association"}; + PipelineDef entTextureDirty {"entTextureDirty"}; PipelineDef entMeshDirty {"entMeshDirty"}; @@ -179,9 +211,11 @@ struct PlShapeSpawn #define TESTAPP_DATA_BOUNDS 2, \ idBounds, idOutOfBounds -#define OSP_TAGS_TESTAPP_BOUNDS 5, \ - tgBoundsSetDel, tgBoundsSetMod, tgBoundsSetReq, \ - tgOutOfBoundsPrv, tgOutOfBoundsMod +struct PlBounds +{ + PipelineDef boundsSet {"boundsSet"}; + PipelineDef outOfBounds {"outOfBounds"}; +}; @@ -302,7 +336,7 @@ struct PlWindowApp idActiveApp, idRenderGl struct PlMagnum { - PipelineDef cleanup {"cleanup Cleanup Magnum"}; + PipelineDef cleanup {"cleanup - Cleanup magnum renderer resources before destruction"}; PipelineDef sync {"sync"}; PipelineDef resync {"resync"}; diff --git a/src/test_application/activescenes/scenarios.cpp b/src/test_application/activescenes/scenarios.cpp index 29abe902..d85bf2b8 100644 --- a/src/test_application/activescenes/scenarios.cpp +++ b/src/test_application/activescenes/scenarios.cpp @@ -162,24 +162,11 @@ static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session c rActiveApp.set_osp_app( std::make_unique(rTestApp, rMainLoopCtrl, mainLoop, inputs, renderSync, sceneUpdate, sceneRender) ); } -template -static constexpr void register_stage_enum() -{ - PipelineInfo::stage_type_t const type = PipelineInfo::stage_type_family_t::value; - PipelineInfo::sm_stageNames[type] = stage_names(STAGE_ENUM_T{}); -} - static ScenarioMap_t make_scenarios() { ScenarioMap_t scenarioMap; - PipelineInfo::sm_stageNames.resize(32); - - register_stage_enum(); - register_stage_enum(); - register_stage_enum(); - register_stage_enum(); - register_stage_enum(); + register_stage_enums(); auto const add_scenario = [&scenarioMap] (std::string_view name, std::string_view desc, SceneSetupFunc_t run) { @@ -237,21 +224,17 @@ static ScenarioMap_t make_scenarios() commonScene = setup_common_scene (builder, rTopData, scene, application, defaultPkg); physics = setup_physics (builder, rTopData, scene, commonScene); shapeSpawn = setup_shape_spawn (builder, rTopData, scene, commonScene, physics, sc_matVisualizer); - //droppers = setup_droppers (builder, rTopData, commonScene, shapeSpawn); - //bounds = setup_bounds (builder, rTopData, commonScene, physics, shapeSpawn); + droppers = setup_droppers (builder, rTopData, scene, commonScene, shapeSpawn); + bounds = setup_bounds (builder, rTopData, scene, commonScene, shapeSpawn); newton = setup_newton (builder, rTopData, scene, commonScene, physics); nwtGravSet = setup_newton_factors (builder, rTopData); - //nwtGrav = setup_newton_force_accel (builder, rTopData, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); + nwtGrav = setup_newton_force_accel (builder, rTopData, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, commonScene, physics, shapeSpawn, newton, nwtGravSet); create_materials(rTopData, commonScene, sc_materialCount); add_floor(rTopData, application, commonScene, shapeSpawn, sc_matVisualizer, defaultPkg); -// auto const tgScn = scene .get_pipelines(); -// auto const tgCS = commonScene .get_pipelines(); -// auto const tgShSp = shapeSpawn .get_pipelines(); - return [] (TestApp& rTestApp) { auto const application = rTestApp.m_application; diff --git a/src/test_application/activescenes/scenarios.h b/src/test_application/activescenes/scenarios.h index e55de350..8a8eb8af 100644 --- a/src/test_application/activescenes/scenarios.h +++ b/src/test_application/activescenes/scenarios.h @@ -42,6 +42,7 @@ namespace scenes using enum EStgOptn; using enum EStgCont; using enum EStgIntr; + using enum EStgRevd; using enum EStgEvnt; using enum EStgFBO; } diff --git a/src/test_application/activescenes/scene_common.cpp b/src/test_application/activescenes/scene_common.cpp index a515c563..9ea64c20 100644 --- a/src/test_application/activescenes/scene_common.cpp +++ b/src/test_application/activescenes/scene_common.cpp @@ -87,6 +87,8 @@ Session setup_common_scene( OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_COMMON_SCENE); auto const tgCS = out.create_pipelines(rBuilder); + out.m_cleanup = tgScn.cleanup; + /* unused */ top_emplace< ActiveEntVec_t > (topData, idActiveEntDel); /* unused */ top_emplace< DrawEntVec_t > (topData, idDrawEntDel); auto &rBasic = top_emplace< ACtxBasic > (topData, idBasic); @@ -104,6 +106,8 @@ Session setup_common_scene( rBuilder.pipeline(tgCS.drawEntDelete) .parent(tgScn.update); rBuilder.pipeline(tgCS.mesh) .parent(tgScn.update); rBuilder.pipeline(tgCS.texture) .parent(tgScn.update); + rBuilder.pipeline(tgCS.entMesh) .parent(tgScn.update); + rBuilder.pipeline(tgCS.entTexture) .parent(tgScn.update); rBuilder.pipeline(tgCS.entTextureDirty) .parent(tgScn.update); rBuilder.pipeline(tgCS.entMeshDirty) .parent(tgScn.update); rBuilder.pipeline(tgCS.meshResDirty) .parent(tgScn.update); @@ -111,9 +115,20 @@ Session setup_common_scene( rBuilder.pipeline(tgCS.material) .parent(tgScn.update); rBuilder.pipeline(tgCS.materialDirty) .parent(tgScn.update); + rBuilder.task() + .name ("Cancel entity delete tasks stuff if no entities were deleted") + .run_on ({tgCS.activeEntDelete(Schedule_)}) + .push_to (out.m_tasks) + .args ({ idBasic, idActiveEntDel }) + .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept + { + return rActiveEntDel.empty() ? TaskAction::Cancel : TaskActions{}; + }); + rBuilder.task() .name ("Delete ActiveEnt IDs") - .run_on ({tgCS.activeEnt(Delete)}) + .run_on ({tgCS.activeEntDelete(EStgIntr::UseOrRun)}) + .sync_with ({tgCS.activeEnt(Delete)}) .push_to (out.m_tasks) .args ({ idBasic, idActiveEntDel }) .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept @@ -127,16 +142,6 @@ Session setup_common_scene( } }); - rBuilder.task() - .name ("Cancel entity delete tasks stuff if no entities were deleted") - .run_on ({tgCS.activeEntDelete(Schedule_)}) - .push_to (out.m_tasks) - .args ({ idBasic, idActiveEntDel }) - .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept - { - return rActiveEntDel.empty() ? TaskAction::Cancel : TaskActions{}; - }); - rBuilder.task() .name ("Delete basic components") .run_on ({tgCS.activeEntDelete(UseOrRun)}) @@ -168,8 +173,8 @@ Session setup_common_scene( rBuilder.task() .name ("Delete drawing components") - .run_on ({tgCS.mesh(Delete)}) - .sync_with ({tgCS.texture(Delete)}) + .run_on ({tgCS.drawEntDelete(UseOrRun)}) + .sync_with ({tgCS.entTexture(Delete), tgCS.entMesh(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawEntDel }) .func([] (ACtxDrawing& rDrawing, DrawEntVec_t const& rDrawEntDel) noexcept @@ -206,7 +211,10 @@ Session setup_common_scene( { for (Material &rMat : rDrawing.m_materials) { - rMat.m_ents.reset(std::size_t(ent)); + if (std::size_t(ent) < rMat.m_ents.size()) + { + rMat.m_ents.reset(std::size_t(ent)); + } } } }); @@ -269,7 +277,6 @@ Session setup_common_scene( rBuilder.task() .name ("Clean up scene and resource owners") .run_on ({tgScn.cleanup(Run_)}) - //.sync_with ({tgCS.mesh(Delete), tgCS.texture(Delete)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idResources}) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources) noexcept diff --git a/src/test_application/activescenes/scene_misc.cpp b/src/test_application/activescenes/scene_misc.cpp index 55a74b84..90b0d117 100644 --- a/src/test_application/activescenes/scene_misc.cpp +++ b/src/test_application/activescenes/scene_misc.cpp @@ -244,7 +244,7 @@ Session setup_thrower( Matrix4 const &camTf = rCamCtrl.m_transform; float const speed = 120; float const dist = 8.0f; - rSpawner.m_spawnRequest.emplace_back(SpawnShape{ + rSpawner.m_spawnRequest.push_back({ .m_position = camTf.translation() - camTf.backward() * dist, .m_velocity = -camTf.backward() * speed, .m_size = Vector3{1.0f}, @@ -257,36 +257,41 @@ Session setup_thrower( return out; } -/* - Session setup_droppers( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& scene, Session const& commonScene, Session const& shapeSpawn) { - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(shapeSpawn, TESTAPP_SHAPE_SPAWN); - OSP_SESSION_UNPACK_TAGS(shapeSpawn, TESTAPP_SHAPE_SPAWN); + OSP_DECLARE_GET_DATA_IDS(scene, TESTAPP_DATA_SCENE); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); - Session droppers; - auto const [idSpawnTimerA, idSpawnTimerB] = droppers.acquire_data<2>(topData); + auto const tgScn = scene .get_pipelines(); + auto const tgShSp = shapeSpawn .get_pipelines(); + + Session out; + auto const [idSpawnTimerA, idSpawnTimerB] = out.acquire_data<2>(topData); top_emplace< float > (topData, idSpawnTimerA, 0.0f); top_emplace< float > (topData, idSpawnTimerB, 0.0f); - droppers.task() = rBuilder.task().assign({tgTimeEvt, tgSpawnMod}).data( - "Spawn blocks every 2 seconds", - TopDataIds_t{ idSpawner, idSpawnTimerA, idDeltaTimeIn }, - wrap_args([] (SpawnerVec_t& rSpawner, float& rSpawnTimer, float const deltaTimeIn) noexcept + rBuilder.task() + .name ("Spawn blocks every 2 seconds") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgShSp.spawnRequest(Modify_)}) + .push_to (out.m_tasks) + .args({ idSpawner, idSpawnTimerA, idDeltaTimeIn }) + .func([] (ACtxShapeSpawner& rSpawner, float& rSpawnTimer, float const deltaTimeIn) noexcept + { rSpawnTimer += deltaTimeIn; if (rSpawnTimer >= 2.0f) { rSpawnTimer -= 2.0f; - rSpawner.emplace_back(SpawnShape{ + rSpawner.m_spawnRequest.push_back({ .m_position = {10.0f, 0.0f, 30.0f}, .m_velocity = {0.0f, 0.0f, 0.0f}, .m_size = {2.0f, 2.0f, 1.0f}, @@ -294,19 +299,22 @@ Session setup_droppers( .m_shape = EShape::Box }); } - })); + }); - droppers.task() = rBuilder.task().assign({tgTimeEvt, tgSpawnMod}).data( - "Spawn cylinders every 1 seconds", - TopDataIds_t{ idSpawner, idSpawnTimerB, idDeltaTimeIn }, - wrap_args([] (SpawnerVec_t& rSpawner, float& rSpawnTimer, float const deltaTimeIn) noexcept + rBuilder.task() + .name ("Spawn cylinders every 1 second") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgShSp.spawnRequest(Modify_)}) + .push_to (out.m_tasks) + .args({ idSpawner, idSpawnTimerB, idDeltaTimeIn }) + .func([] (ACtxShapeSpawner& rSpawner, float& rSpawnTimer, float const deltaTimeIn) noexcept { rSpawnTimer += deltaTimeIn; if (rSpawnTimer >= 1.0f) { rSpawnTimer -= 1.0f; - rSpawner.emplace_back(SpawnShape{ + rSpawner.m_spawnRequest.push_back({ .m_position = {-10.0f, 0.0, 30.0f}, .m_velocity = {0.0f, 0.0f, 0.0f}, .m_size = {2.0f, 2.0f, 1.0f}, @@ -314,9 +322,114 @@ Session setup_droppers( .m_shape = EShape::Cylinder }); } - })); + }); + + return out; +} - return droppers; -}*/ +Session setup_bounds( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& scene, + Session const& commonScene, + Session const& shapeSpawn) +{ + using ActiveEntVec_t = std::vector; + + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); + auto const tgScn = scene .get_pipelines(); + auto const tgCS = commonScene .get_pipelines(); + auto const tgShSp = shapeSpawn .get_pipelines(); + + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_BOUNDS); + auto const tgBnds = out.create_pipelines(rBuilder); + + rBuilder.pipeline(tgBnds.boundsSet) .parent(tgScn.update); + rBuilder.pipeline(tgBnds.outOfBounds) .parent(tgScn.update); + + top_emplace< ActiveEntSet_t > (topData, idBounds); + top_emplace< ActiveEntVec_t > (topData, idOutOfBounds); + + rBuilder.task() + .name ("Check for out-of-bounds entities") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgCS.transform(Ready), tgBnds.boundsSet(Ready), tgBnds.outOfBounds(Modify__)}) + .push_to (out.m_tasks) + .args ({ idBasic, idBounds, idOutOfBounds }) + .func([] (ACtxBasic const& rBasic, ActiveEntSet_t const& rBounds, ActiveEntVec_t& rOutOfBounds) noexcept + { + for (std::size_t const ent : rBounds.ones()) + { + ACompTransform const &entTf = rBasic.m_transform.get(ActiveEnt(ent)); + if (entTf.m_transform.translation().z() < -10) + { + rOutOfBounds.push_back(ActiveEnt(ent)); + } + } + }); + + rBuilder.task() + .name ("Queue-Delete out-of-bounds entities") + .run_on ({tgBnds.outOfBounds(UseOrRun_)}) + .sync_with ({tgCS.activeEntDelete(Modify_), tgCS.hierarchy(Delete)}) + .push_to (out.m_tasks) + .args ({ idBasic, idActiveEntDel, idOutOfBounds }) + .func([] (ACtxBasic& rBasic, ActiveEntVec_t& rActiveEntDel, ActiveEntVec_t& rOutOfBounds) noexcept + { + SysSceneGraph::queue_delete_entities(rBasic.m_scnGraph, rActiveEntDel, rOutOfBounds.begin(), rOutOfBounds.end()); + }); + + rBuilder.task() + .name ("Clear out-of-bounds vector once we're done with it") + .run_on ({tgBnds.outOfBounds(Clear_)}) + .push_to (out.m_tasks) + .args ({ idOutOfBounds }) + .func([] (ActiveEntVec_t& rOutOfBounds) noexcept + { + rOutOfBounds.clear(); + }); + + rBuilder.task() + .name ("Add bounds to spawned shapes") + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgBnds.boundsSet(Modify)}) + .push_to (out.m_tasks) + .args ({ idBasic, idSpawner, idBounds }) + .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner, ActiveEntSet_t& rBounds) noexcept + { + rBounds.ints().resize(rBasic.m_activeIds.vec().capacity()); + + for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) + { + SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; + if (spawn.m_mass == 0) + { + continue; + } + + ActiveEnt const root = rSpawner.m_ents[i * 2]; + + rBounds.set(std::size_t(root)); + } + }); + + rBuilder.task() + .name ("Delete bounds components") + .run_on ({tgCS.activeEntDelete(UseOrRun)}) + .sync_with ({tgBnds.boundsSet(Delete)}) + .push_to (out.m_tasks) + .args ({ idActiveEntDel, idBounds }) + .func([] (ActiveEntVec_t const& rActiveEntDel, ActiveEntSet_t& rBounds) noexcept + { + for (osp::active::ActiveEnt const ent : rActiveEntDel) + { + rBounds.reset(std::size_t(ent)); + } + }); + + return out; +} } // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_misc.h b/src/test_application/activescenes/scene_misc.h index e8427ff8..e86e8ae1 100644 --- a/src/test_application/activescenes/scene_misc.h +++ b/src/test_application/activescenes/scene_misc.h @@ -79,7 +79,19 @@ osp::Session setup_thrower( osp::Session setup_droppers( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& scene, + osp::Session const& commonScene, + osp::Session const& shapeSpawn); + +/** + * @brief Entity set to delete entities under Z = -10, added to spawned shapes + */ +osp::Session setup_bounds( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& scene, osp::Session const& commonScene, osp::Session const& shapeSpawn); + } diff --git a/src/test_application/activescenes/scene_newton.cpp b/src/test_application/activescenes/scene_newton.cpp index 773bcde6..2c89fbcc 100644 --- a/src/test_application/activescenes/scene_newton.cpp +++ b/src/test_application/activescenes/scene_newton.cpp @@ -96,7 +96,7 @@ Session setup_newton( rBuilder.task() .name ("Update Newton world") .run_on ({tgScn.update(Run)}) - .sync_with ({/*tgNwt.nwtBody(Modify),*/ tgCS.hierarchy(Modify), tgPhy.physUpdate(Run), tgCS.transform(Modify)}) + .sync_with ({tgNwt.nwtBody(Prev), tgCS.hierarchy(Prev), tgPhy.physBody(Prev), tgPhy.physUpdate(Run), tgCS.transform(Prev)}) .push_to (out.m_tasks) .args({ idBasic, idPhys, idNwt, idDeltaTimeIn }) .func([] (ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, float const deltaTimeIn, WorkerContext ctx) noexcept @@ -125,48 +125,48 @@ osp::Session setup_newton_factors( return out; } -//osp::Session setup_newton_force_accel( -// TopTaskBuilder& rBuilder, -// ArrayView topData, -// Session const& newton, -// Session const& nwtFactors, -// Vector3 accel) -//{ -// using UserData_t = ACtxNwtWorld::ForceFactorFunc::UserData_t; -// OSP_SESSION_UNPACK_DATA(newton, TESTAPP_NEWTON); -// OSP_SESSION_UNPACK_DATA(nwtFactors, TESTAPP_NEWTON_FORCES); - -// auto &rNwt = top_get(topData, idNwt); +osp::Session setup_newton_force_accel( + TopTaskBuilder& rBuilder, + ArrayView topData, + Session const& newton, + Session const& nwtFactors, + Vector3 accel) +{ + using UserData_t = ACtxNwtWorld::ForceFactorFunc::UserData_t; + OSP_DECLARE_GET_DATA_IDS(newton, TESTAPP_DATA_NEWTON); + OSP_DECLARE_GET_DATA_IDS(nwtFactors, TESTAPP_DATA_NEWTON_FORCES); -// Session nwtAccel; -// OSP_SESSION_ACQUIRE_DATA(nwtAccel, topData, TESTAPP_NEWTON_ACCEL); + auto &rNwt = top_get(topData, idNwt); -// auto &rAccel = top_emplace(topData, idAcceleration, accel); + Session nwtAccel; + OSP_DECLARE_CREATE_DATA_IDS(nwtAccel, topData, TESTAPP_DATA_NEWTON_ACCEL); -// ACtxNwtWorld::ForceFactorFunc const factor -// { -// .m_func = [] (NewtonBody const* pBody, BodyId const bodyID, ACtxNwtWorld const& rNwt, UserData_t data, Vector3& rForce, Vector3& rTorque) noexcept -// { -// float mass = 0.0f; -// float dummy = 0.0f; -// NewtonBodyGetMass(pBody, &mass, &dummy, &dummy, &dummy); + auto &rAccel = top_emplace(topData, idAcceleration, accel); -// auto const& force = *reinterpret_cast(data[0]); -// rForce += force * mass; -// }, -// .m_userData = {&rAccel} -// }; + ACtxNwtWorld::ForceFactorFunc const factor + { + .m_func = [] (NewtonBody const* pBody, BodyId const bodyID, ACtxNwtWorld const& rNwt, UserData_t data, Vector3& rForce, Vector3& rTorque) noexcept + { + float mass = 0.0f; + float dummy = 0.0f; + NewtonBodyGetMass(pBody, &mass, &dummy, &dummy, &dummy); + + auto const& force = *reinterpret_cast(data[0]); + rForce += force * mass; + }, + .m_userData = {&rAccel} + }; -// // Register force + // Register force -// std::size_t const index = rNwt.m_factors.size(); -// rNwt.m_factors.emplace_back(factor); + std::size_t const index = rNwt.m_factors.size(); + rNwt.m_factors.emplace_back(factor); -// auto factorBits = lgrn::bit_view(top_get(topData, idNwtFactors)); -// factorBits.set(index); + auto factorBits = lgrn::bit_view(top_get(topData, idNwtFactors)); + factorBits.set(index); -// return nwtAccel; -//} + return nwtAccel; +} Session setup_shape_spawn_newton( @@ -186,7 +186,6 @@ Session setup_shape_spawn_newton( OSP_DECLARE_GET_DATA_IDS(nwtFactors, TESTAPP_DATA_NEWTON_FORCES); auto const tgPhy = physics .get_pipelines(); - auto const tgShSp = shapeSpawn .get_pipelines(); auto const tgNwt = newton .get_pipelines(); diff --git a/src/test_application/activescenes/scene_physics.cpp b/src/test_application/activescenes/scene_physics.cpp index b470c97b..814e4d8e 100644 --- a/src/test_application/activescenes/scene_physics.cpp +++ b/src/test_application/activescenes/scene_physics.cpp @@ -110,7 +110,7 @@ Session setup_shape_spawn( .run_on ({tgShSp.spawnRequest(UseOrRun)}) .sync_with ({tgCS.activeEnt(New), tgShSp.spawnedEnts(Resize)}) .push_to (out.m_tasks) - .args ({ idBasic, idSpawner }) + .args ({ idBasic, idSpawner}) .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept { //LGRN_ASSERTM(!rSpawner.m_spawnRequest.empty(), "spawnRequest Use_ shouldn't run if rSpawner.m_spawnRequest is empty!"); @@ -147,7 +147,7 @@ Session setup_shape_spawn( .name ("Add mesh and material to spawned shapes") .run_on ({tgShSp.spawnRequest(UseOrRun)}) .sync_with ({tgShSp.spawnedEnts(UseOrRun), - tgCS.mesh(New), tgCS.material(New), tgCS.drawEnt(New), tgCS.drawEntResized(ModifyOrSignal), + tgCS.entMesh(New), tgCS.material(New), tgCS.drawEnt(New), tgCS.drawEntResized(ModifyOrSignal), tgCS.materialDirty(Modify_), tgCS.entMeshDirty(Modify_)}) .push_to (out.m_tasks) .args ({ idBasic, idDrawing, idSpawner, idNMesh }) @@ -365,94 +365,6 @@ Session setup_prefabs( return prefabs; } -Session setup_bounds( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Tags& rTags, - Session const& commonScene, - Session const& physics, - Session const& shapeSpawn) -{ - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_DATA(shapeSpawn, TESTAPP_SHAPE_SPAWN); - OSP_SESSION_UNPACK_TAGS(shapeSpawn, TESTAPP_SHAPE_SPAWN); - - Session bounds; - OSP_SESSION_ACQUIRE_DATA(bounds, topData, TESTAPP_BOUNDS); - OSP_SESSION_ACQUIRE_TAGS(bounds, rTags, TESTAPP_BOUNDS); - - top_emplace< EntSet_t > (topData, idBounds); - top_emplace< EntVector_t > (topData, idOutOfBounds); - - rBuilder.tag(tgBoundsSetMod) .depend_on({tgBoundsSetDel}); - rBuilder.tag(tgBoundsSetReq) .depend_on({tgBoundsSetDel, tgBoundsSetMod}); - rBuilder.tag(tgOutOfBoundsMod) .depend_on({tgOutOfBoundsPrv}); - - // Bounds are checked are after transforms are final (PlransformReq) - // Out-of-bounds entities are added to rOutOfBounds, and are deleted - // at the start of next frame - - bounds.task() = rBuilder.task().assign({tgSceneEvt, PlransformReq, tgBoundsSetReq, tgOutOfBoundsMod}).data( - "Check for out-of-bounds entities", - TopDataIds_t{ idBasic, idBounds, idOutOfBounds}, - wrap_args([] (ACtxBasic const& rBasic, EntSet_t const& rBounds, EntVector_t& rOutOfBounds) noexcept - { - rOutOfBounds.clear(); - for (std::size_t const ent : rBounds.ones()) - { - ACompTransform const &entTf = rBasic.m_transform.get(ActiveEnt(ent)); - if (entTf.m_transform.translation().z() < -10) - { - rOutOfBounds.push_back(ActiveEnt(ent)); - } - } - })); - - - bounds.task() = rBuilder.task().assign({tgSceneEvt, tgOutOfBoundsPrv, tgDelEntMod}).data( - "Delete out-of-bounds entities", - TopDataIds_t{ idBasic, idOutOfBounds, idDelEnts }, - wrap_args([] (ACtxBasic const& rBasic, EntVector_t const& rOutOfBounds, EntVector_t& rDelEnts) noexcept - { - rDelEnts.insert(rDelEnts.end(), rOutOfBounds.cbegin(), rOutOfBounds.cend()); - })); - - bounds.task() = rBuilder.task().assign({tgSceneEvt, tgSpawnReq, tgBoundsSetMod}).data( - "Add bounds to spawned shapes", - TopDataIds_t{ idSpawner, idSpawnerEnts, idBounds, idActiveIds }, - wrap_args([] (SpawnerVec_t const& rSpawner, EntVector_t const& rSpawnerEnts, EntSet_t& rBounds, ActiveReg_t const& rActiveIds) noexcept - { - rBounds .ints().resize(rActiveIds.vec().capacity()); - - for (std::size_t i = 0; i < rSpawner.size(); ++i) - { - SpawnShape const &spawn = rSpawner[i]; - if (spawn.m_mass == 0) - { - continue; - } - - ActiveEnt const root = rSpawnerEnts[i * 2]; - - rBounds.set(std::size_t(root)); - } - })); - - bounds.task() = rBuilder.task().assign({tgSceneEvt, tgDelTotalReq, tgBoundsSetDel}).data( - "Delete bounds components", - TopDataIds_t{idBounds, idDelTotal}, delete_ent_set); -// for (osp::active::ActiveEnt const ent : rDelTotal) -// { -// rSet.reset(std::size_t(ent)); -// } - - return bounds; -} - #endif } // namespace testapp::scenes - - diff --git a/src/test_application/activescenes/scene_physics.h b/src/test_application/activescenes/scene_physics.h index aae237ff..552830c7 100644 --- a/src/test_application/activescenes/scene_physics.h +++ b/src/test_application/activescenes/scene_physics.h @@ -94,15 +94,4 @@ osp::Session setup_prefabs( osp::Session const& material, osp::TopDataId idResources); -/** - * @brief Entity set to delete entities under Z = -10, added to spawned shapes - */ -osp::Session setup_bounds( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& commonScene, - osp::Session const& physics, - osp::Session const& shapeSpawn); - - } // namespace testapp::scenes diff --git a/src/test_application/activescenes/scene_renderer.cpp b/src/test_application/activescenes/scene_renderer.cpp index ee6e70c3..b2cf7af5 100644 --- a/src/test_application/activescenes/scene_renderer.cpp +++ b/src/test_application/activescenes/scene_renderer.cpp @@ -99,6 +99,7 @@ Session setup_magnum( Session out; OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_MAGNUM); auto const tgMgn = out.create_pipelines(rBuilder); + out.m_cleanup = tgMgn.cleanup; rBuilder.pipeline(tgMgn.sync) .parent(tgApp.mainLoop).wait_for_signal(ModifyOrSignal); @@ -165,8 +166,6 @@ Session setup_scene_renderer( OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); auto const tgApp = application .get_pipelines< PlApplication >(); - auto const tgWin = windowApp .get_pipelines< PlWindowApp >(); - auto const tgScn = scene .get_pipelines< PlScene >(); auto const tgCS = commonScene .get_pipelines< PlCommonScene >(); auto const tgMgn = magnum .get_pipelines< PlMagnum >(); @@ -233,7 +232,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Schedule Assign GL textures") .schedules ({tgCS.entTextureDirty(Schedule_)}) - .sync_with ({tgCS.texture(Ready)}) + .sync_with ({tgCS.texture(Ready), tgCS.entTexture(Ready)}) .push_to (out.m_tasks) .args ({ idDrawing }) .func([] (ACtxDrawing& rDrawing) noexcept -> TaskActions @@ -244,7 +243,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Sync GL textures to entities with scene textures") .run_on ({tgCS.entTextureDirty(UseOrRun)}) - .sync_with ({tgCS.texture(Ready), tgMgn.textureGL(Ready), tgMgn.entTextureGL(Modify), tgCS.drawEntResized(Done)}) + .sync_with ({tgCS.texture(Ready), tgCS.entTexture(Ready), tgMgn.textureGL(Ready), tgMgn.entTextureGL(Modify), tgCS.drawEntResized(Done)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept @@ -280,7 +279,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Schedule Assign GL meshes") .schedules ({tgCS.entMeshDirty(Schedule_)}) - .sync_with ({tgCS.mesh(Ready)}) + .sync_with ({tgCS.mesh(Ready), tgCS.entMesh(Ready)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept -> TaskActions @@ -291,7 +290,7 @@ Session setup_scene_renderer( rBuilder.task() .name ("Sync GL meshes to entities with scene meshes") .run_on ({tgCS.entMeshDirty(UseOrRun)}) - .sync_with ({tgCS.mesh(Ready), tgMgn.meshGL(Ready), tgMgn.entMeshGL(Modify), tgCS.drawEntResized(Done)}) + .sync_with ({tgCS.mesh(Ready), tgCS.entMesh(Ready), tgMgn.meshGL(Ready), tgMgn.entMeshGL(Modify), tgCS.drawEntResized(Done)}) .push_to (out.m_tasks) .args ({ idDrawing, idDrawingRes, idScnRender, idRenderGl }) .func([] (ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRenderGL& rScnRender, RenderGL& rRenderGl) noexcept diff --git a/src/test_application/main.cpp b/src/test_application/main.cpp index 04206ef4..73ecb837 100644 --- a/src/test_application/main.cpp +++ b/src/test_application/main.cpp @@ -209,7 +209,13 @@ void debug_cli_loop() { std::cout << "Loading scene: " << it->first << "\n"; - //g_testApp.close_sessions(g_executor, g_testApp.m_scene); // Close existing scene + // Close existing scene first + if ( ! g_testApp.m_scene.m_sessions.empty() ) + { + g_testApp.close_sessions(g_testApp.m_scene.m_sessions); + g_testApp.m_scene.m_sessions.clear(); + g_testApp.m_scene.m_edges.m_syncWith.clear(); + } g_testApp.m_rendererSetup = it->second.m_setup(g_testApp); start_magnum_async(); diff --git a/src/test_application/testapp.cpp b/src/test_application/testapp.cpp index 1edb7dfd..753623be 100644 --- a/src/test_application/testapp.cpp +++ b/src/test_application/testapp.cpp @@ -77,6 +77,9 @@ void TestApp::close_sessions(osp::ArrayView const sessions) for (PipelineId const pipeline : rSession.m_pipelines) { m_tasks.m_pipelineIds.remove(pipeline); + m_tasks.m_pipelineParents[pipeline] = lgrn::id_null(); + m_tasks.m_pipelineInfo[pipeline] = {}; + m_tasks.m_pipelineControl[pipeline] = {}; } rSession.m_pipelines.clear(); } From c1759949086ebd09e80f61eb495434c2d202ad17 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Sun, 27 Aug 2023 21:58:18 -0700 Subject: [PATCH 35/35] Fix windows build and other issues on PR #244 --- src/osp/tasks/builder.h | 1 + src/osp/tasks/tasks.h | 6 +++--- src/osp/tasks/top_execute.cpp | 23 +++++++++++------------ src/osp/tasks/top_session.h | 2 +- src/test_application/MagnumApplication.h | 7 +------ 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/osp/tasks/builder.h b/src/osp/tasks/builder.h index 503d4a08..3bd2a794 100644 --- a/src/osp/tasks/builder.h +++ b/src/osp/tasks/builder.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include diff --git a/src/osp/tasks/tasks.h b/src/osp/tasks/tasks.h index 76320d88..957f262a 100644 --- a/src/osp/tasks/tasks.h +++ b/src/osp/tasks/tasks.h @@ -41,8 +41,8 @@ #define OSP_DECLARE_STAGE_NAMES(type, ...) \ inline osp::ArrayView stage_names([[maybe_unused]] type _) noexcept \ { \ - static auto const arr = std::array{__VA_ARGS__}; \ - return osp::arrayView( arr.data(), arr.size() ); \ + static auto const arr = std::initializer_list{__VA_ARGS__}; \ + return osp::arrayView(arr); \ } #define OSP_DECLARE_STAGE_SCHEDULE(type, schedule_enum) \ @@ -92,7 +92,7 @@ struct PipelineInfo std::string_view name; std::string_view category; - stage_type_t stageType; + stage_type_t stageType { lgrn::id_null() }; }; struct PipelineControl diff --git a/src/osp/tasks/top_execute.cpp b/src/osp/tasks/top_execute.cpp index 512b2bd6..ebcc4e7b 100644 --- a/src/osp/tasks/top_execute.cpp +++ b/src/osp/tasks/top_execute.cpp @@ -32,7 +32,6 @@ #include #include -#include #include #include @@ -110,8 +109,6 @@ std::ostream& operator<<(std::ostream& rStream, TopExecWriteState const& write) { ExecPipeline const &plExec = exec.plData[pipeline]; - std::cout << "pipeline: " << int(pipeline) << "\n"; - for (int i = 0; i < depth; ++i) { rStream << "- "; @@ -146,25 +143,27 @@ std::ostream& operator<<(std::ostream& rStream, TopExecWriteState const& write) PipelineInfo const& info = tasks.m_pipelineInfo[pipeline]; - auto const stageNames = ArrayView{PipelineInfo::sm_stageNames[info.stageType]}; - int charsUsed = 7; // "PL###" + ": " - for (int stage = 0; stage < std::min(stageNames.size(), stageCount); ++stage) + if (info.stageType != lgrn::id_null()) { - bool const sel = int(plExec.stage) == stage; - rStream << (sel ? '[' : ' ') - << stageNames[stage] - << (sel ? ']' : ' '); + auto const stageNames = ArrayView{PipelineInfo::sm_stageNames[info.stageType]}; - charsUsed += 2 + stageNames[stage].size(); + for (int stage = 0; stage < std::min(stageNames.size(), stageCount); ++stage) + { + bool const sel = int(plExec.stage) == stage; + rStream << (sel ? '[' : ' ') + << stageNames[stage] + << (sel ? ']' : ' '); + + charsUsed += 2 + stageNames[stage].size(); + } } for (; charsUsed < nameMinColumns; ++charsUsed) { rStream << ' '; } - std::cout << "AAAA" << info.name; rStream << " | " << (info.name.empty() ? "untitled or deleted" : info.name); diff --git a/src/osp/tasks/top_session.h b/src/osp/tasks/top_session.h index e59b410a..0a882099 100644 --- a/src/osp/tasks/top_session.h +++ b/src/osp/tasks/top_session.h @@ -100,7 +100,7 @@ struct Session static_assert(sizeof(TGT_STRUCT_T) % sizeof(PipelineId) == 0); constexpr std::size_t count = sizeof(TGT_STRUCT_T) / sizeof(PipelineDefBlank_t); - std::type_info const& info = typeid(TGT_STRUCT_T); + [[maybe_unused]] std::type_info const& info = typeid(TGT_STRUCT_T); LGRN_ASSERTMV(m_structHash == info.hash_code() && count == m_pipelines.size(), "get_pipeline must use the same struct previously given to get_pipelines", m_structHash, m_structName, diff --git a/src/test_application/MagnumApplication.h b/src/test_application/MagnumApplication.h index dc60483a..05adeb4e 100644 --- a/src/test_application/MagnumApplication.h +++ b/src/test_application/MagnumApplication.h @@ -56,15 +56,10 @@ class IOspApplication * * This is intended to run a flight scene, map view, vehicle editor, or menu. */ -class MagnumApplication : Magnum::Platform::Application +class MagnumApplication : public Magnum::Platform::Application { - public: - using Magnum::Platform::Application::KeyEvent; - using Magnum::Platform::Application::MouseEvent; - using Magnum::Platform::Application::Arguments; - using AppPtr_t = std::unique_ptr; explicit MagnumApplication(